1

I need to reverse a for loop in a bash script. There is a directory with videos named after %Y%m.mp4 (201701.mp4; 201702.mp4, 201703.mp4, ...). The loop should start with the oldest filename (e.g. 201712.mp4) How to reverse my for loop?

outputdir="/path/to/video/monthly/"

for file in "$outputdir"*.mp4
do

echo $file

done
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
mbergmann
  • 105
  • 2
  • 8
  • https://unix.stackexchange.com/questions/27382/how-do-i-reverse-a-for-loop – underscore_d Dec 06 '17 at 13:52
  • Thank you for the fast respons. I tryed your solutions already but I get just the path without any filename. That's why I asking here. – mbergmann Dec 06 '17 at 13:57
  • 2
    It would make sense to explain in your post what common solutions you have tried and why they didn't work. Then people won't have to tell you things that you already ruled out. – underscore_d Dec 06 '17 at 14:03

1 Answers1

5

You can list your files in a reserved order in the following way:

ls -1 $outputdir/*.mp4 | sort -r

So in you script you can do:

outputdir="/path/to/video/monthly/"
for file in $(ls -1 $outputdir*.mp4 | sort -r)
do
   echo $file
done

NOTE: As @PesaThe pointed out this solution would fail to work with filenames with spaces. If it is the case for you, you should quote the command: "$(ls -1 $outputdir*.mp4 | sort -r)" and use "$file"

UPDATE: See the following test

mkdir test && cd $_
for i in {1..5}; do touch test_$i.txt; done
cd -

And ls -1 test/*.txt will output:

test/test_1.txt
test/test_2.txt
test/test_3.txt
test/test_4.txt
test/test_5.txt

And ls -1 test/*txt | sort -r will output:

test/test_5.txt
test/test_4.txt
test/test_3.txt
test/test_2.txt
test/test_1.txt
marcell
  • 1,498
  • 1
  • 10
  • 22
  • 3
    Note: `ls -r` is a way to make ls reverse sorting. – wildplasser Dec 06 '17 at 14:07
  • 1
    You can also do `touch test_{1..5}.txt` – PesaThe Dec 06 '17 at 14:08
  • 1
    And you might mention in your answer that your solution will fail for paths/files with, for example, spaces in them. – PesaThe Dec 06 '17 at 14:12
  • By the way, regarding your update...just quoting the expansion "$(...)" will take all the output of ls as one "file". So unfortunately that won't work either. – PesaThe Dec 06 '17 at 18:48
  • Maybe parsing the output of `ls` in order to iterate through files is not the best idea, however with `ls (GNU coreutils) 8.21` and `BASH_VERSION 4.3.11(1)-release` on `Ubuntu 14.04.5 LTS` the following example is working correctly: `mkdir test && cd $_; touch "test "{1..5}.txt; cd -` and after that `for f in "$(ls -1 test/*txt | sort -r)"; do echo "$f"; done` But this fails: `for f in $(ls -1 test/*txt | sort -r); do echo "$f"; done` – marcell Dec 07 '17 at 15:54
  • 1
    @marcell read my comment above please. This **DOES NOT** work. When you quote the expansion, the entire output is regarded as ONE argument. Therefore, your `for` iterates over just that one argument, not the individual files. It has the same functionality as executing: `echo "$(ls -1 test/*.txt | sort -r)"`. And that's a problem as soon as you want to manipulate with the files in any way... – PesaThe Dec 08 '17 at 01:32
  • 1
    @PesaThe I got your point. Thanks for pointing out the issue. The above example just looking the representation and didn't try manipulate the files, which would fail. So, to stuck close to the `ls` solution we should alter the `IFS` internal field separator ***before*** the `for` loop in order to only split the input on newlines and omit quoting the expansion. Thus this would do: `IFS=$'\n'; for f in $(ls -1 test/*txt | sort -r ); do echo file: $f; cat $f; done` but the original `IFS` should be ***reset*** ***after*** the loop. – marcell Dec 08 '17 at 06:45
  • 2
    @marcell this is much better, however, doesn't work with filenames with newlines ;) – PesaThe Dec 08 '17 at 10:10