2

I have a bash script that look out for files recursively under multi-sub folders, then use ffmpeg to each file. at debug, it works great if i use echo ffmpeg but the thing is, at real work where it actually needs to use ffmpeg and wait for half an hour to finish for each video, the bash script cannot successfully run the ffmpeg to each file

My snippet is below:

find * \( -name '*.mkv' -o -name '*avi' -o -name '*mp4' -o -name '*flv' -o -name '*ogg' -o -name '*mov' ! -name '*-[900p].mkv' \) -print |
while IFS= read file    ## IFS= prevents "read" stripping whitespace
do
    ffmpeg -i "$file" "$target" && rm -rf "$file"
done

the thing is, because ffmpeg takes too long to respond, and while would just keep listing and executing. So I'm hoping what can I do to make sure loop would be process only after the done has ended?

Can somebody please help? Thanks!

Geez, I do not know why it was tagged as possible duplicate with another question of mine: Converting `for file in` to `find` so that my script can apply recursively

I am asking for a loop action that would allow my bash script to wait for each function inside the loop to finish before iterating to the next object. It is very far from asking how to find files recursively. Thanks!

arvil
  • 670
  • @Costas very far from the topic of mine. Hoped I expressed my problem and scenario well. – arvil Apr 16 '15 at 18:35
  • 2
    Do you mean you would like find to complete before the first call to ffmpeg happens? As it is, the loop will wait for ffmpeg to complete before processing the next file, so it's not very clear what you are asking. – dhag Apr 16 '15 at 18:59
  • @dhag yeah! seems like that's the idea and feasible. – arvil Apr 16 '15 at 19:01
  • 1
    Why are you using find * (asterisk) ? Why do you need the -r with rm to remove a file ? And while..read is the worst option... Try something like: find . \( -name '*avi' -o -name .... \) -exec sh -c 'ffmpeg -i "$0" "${0%.*}.mkv" && rm -f "$0"' {} \; – don_crissti Apr 16 '15 at 19:15
  • @don_crissti you're rught, i need to cleanup my code. thanks – arvil Apr 16 '15 at 19:19

2 Answers2

4

Instead of piping find into the loop, you could go through an intermediate file. That will ensure that the finding step and the looping step happen in sequence, with no overlap. Something like the following (I altered your expression for brevity):

find \( -name '*.mkv' -o -name '*avi' \) >files
<files while IFS= read file
do
    ffmpeg -i "$file" "$target" && rm -f "$file"
done

Note that this still isn't completely safe; using find -exec would be simpler if your requirements didn't include running find strictly before the first call to ffmpeg. With this requirement, using find -print0 along with xargs or GNU parallel could do the job.

dhag
  • 15,736
  • 4
  • 55
  • 65
  • 2
    I'm super-unsure this is what you are asking... Don't hesitate to comment if I'm answering the wrong question. – dhag Apr 16 '15 at 19:10
  • yes! I just want to make sure they happen in sequence and do not overlap, exactly as you said. thanks! – arvil Apr 16 '15 at 19:12
1

The reason the code is failing to run properly is the mismatch of AND and OR groups in the initial find command:

find * \( -name '*.mkv' -o -name '*avi' -o -name '*mp4' -o -name '*flv' -o -name '*ogg' -o -name '*mov' ! -name '*-[900p-by-ZiriusPH].mkv' \) -print

The binding for the parameters is to print the files matched for

  • Name *.mkv
  • OR Name *.avi
  • OR Name *mp4
  • OR Name *flv
  • OR Name *.ogg
  • OR Name *.ogg
  • OR ( Name *.mov AND Name NOT *-[900p-by-ZiriusPH].mkv )

The last condition will be true for many files, which I suspect is not what you wanted. If you move the AND NOT condition outside the bracketed OR conditions you'll find it works as you expected.

find * \( -name '*.mkv' -o -name '*avi' -o -name '*mp4' -o -name '*flv' -o -name '*ogg' -o -name '*mov' \) ! -name '*-[900p-by-ZiriusPH].mkv' -print
Chris Davies
  • 116,213
  • 16
  • 160
  • 287