Search code examples
bashjpegoptim

Request for input on bash script loop


I am trying to run jpegoptim against pictures and at some point in the loop...after let's say 200 iterations, i get "stdin", which requires input to go further.

Is there a way to force the input?

#!/bin/bash
for i in `find . -name "*.jpg" -type f`; do
  jpegoptim "$i" >> jpg.log; done
done

Solution

  • First, don't process dynamically generated file lists in a for loop.

    Second, your code is broken - you have 2 done's.

    Third, my apologies, not familiar with the tool, but I'm inclined to think your issue is in a broader piece of the code than presented.

    Fourth, (getting minor and nitpicky :) prefer $(...) over backticks. There is very rarely a reason not to do so in bash.

    Still, almost none of that is relevant to your question, aside from maybe the third thing... See if this helps -

    find . -name "*.jpg" -type f |
      while read -r file; do jpegoptim "$file"; done >> jpg.log 2>&1
    

    Or possibly better,

    find . -name "*.jpg" -type f | xargs jpegoptim  >> jpg.log 2>&1
    

    Update

    Benjamin W. points out that these break when filenames have embedded newlines, which is entirely true. I consider filenames with embedded newlines a heinous heresy of the highest order, nut sometimes you don't have control of that, so per his entirely valid suggestion:

    find . -name "*.jpg" -type f |
      while read -r -d '' file; do jpegoptim "$file"; done >> jpg.log 2>&1
    

    or

    find . -name "*.jpg" -type f -print0 | xargs -0 jpegoptim  >> jpg.log 2>&1
    

    or best, for simplicity and (therefore) safety

    find . -name "*.jpg" -type f -exec jpegoptim {} \; >> jpg.log 2>&1
    

    (Please check my syntax on those...)

    Though there are still efficiency considerations if you are processing a large number of files. One should also consider the possibility that the target program may or may not be able to multiprocess command-line arguments. Consider the difference between these:

    $: find /tmp -type f -exec echo {} \;
    /tmp/.mintty-version
    /tmp/AdobeARM.log
    /tmp/foo
    . . . 
    
    $: find /tmp -type f | xargs echo
    /tmp/.mintty-version /tmp/AdobeARM.log /tmp/foo ...