find . -maxdepth 1 -type f -exec identify -regard-warnings -verbose {} > /dev/null 2>&1 + || mv "$1" --backup=numbered {} -t "./Corrupted"   

I'm trying to use the above to check for the existence of corrupted JPEG images in the current directory, and if they exist, move them to another directory.

As far as I can tell the issue so far is that the logic simply isn't working - nothing to the right of the || operator is executed at all when identify exits with a non-zero error code, even when I test with a simple || echo "File is bad".

I understand I can use a second -exec predicate to execute a command if the first -exec turns out to be true, but is there any way to execute a command if -exec turns out to be false?

  • Are you aware {} after || is just a literal {}? Do you want to move all files? or just bad files? Are you aware > /dev/null 2>&1 redirect the output of find in the first place? I mean: not only identify (although it shouldn't matter here). – Kamil Maciorowski Jan 14 '20 at 17:16
  • I was aware of the first but not the second. As for the first, I just left it in there because I knew no other way of getting the argument from the command before the ||. – Hashim Aziz Jan 14 '20 at 17:29

You can exec a bash -c, something like:

find [other parm] -exec bash -c 'identify -regard-warnings -verbose "$1" || exit 0 ; exit 1' onTheFlyScript {} \; -exec mv {} ./Corrupted \;

In slo-mo:

  • identify -regard-warnings -verbose "$1" || exit 0 ; exit 1 reverses the output logic of identify (0 if error)
  • -exec bash -c '...' onTheFlyScript {} \; sets $0 to "onTheFlyScript" (ignored unless there are errors) and $1 to your file, so it succeeds (rc=0) if the file is corrupt
  • -exec mv {} ./Corrupted \; is executed if the first exec succeeds so if the file is corrupt

Given the output of identify on good files, I would avoid the -verbose parm here, it only slows down the process.

Edit: simpler version, no need for the bash -c (which would still be unavoidable in more complex cases):

find . -maxdepth 1 -name '*.jpg' \( -exec identify -quiet {} \; -o -exec mv -t ./Corrupted {} \+ \) 

In other words, the 2nd -exec is executed when the first fails.

As Kamil says in the comments, you cannot mass-identify the files, since you need a status for each, but you can indeed combine a ; and a +, the second one is indeed executed with as many parameters as possible.

  • I was hoping there would be a simpler solution than spawning another shell, but thanks for the only working solution so far. This does work, but it's also pretty slow at just over 1 file a second. How would you make this work with the exec... + syntax to make it run a little faster? I tried a simple swap of the \;s with + and \+ but the command fails with "missing argument to -exec". – Hashim Aziz Jan 14 '20 at 19:11
  • @Hashim Without inner shells it may be find . -maxdepth 1 -type f ! -exec identify -regard-warnings {} \; -exec mv -t ./Corrupted {} +. Not tested. There are reasons not to use -exec … + with identify; one reason: you want to test each file separately, not all of them with a single exit status. But I think you can use + with the final mv. It's not an answer because: (1) not tested, (2) no time for high quality answer now. – Kamil Maciorowski Jan 14 '20 at 19:41
  • @KamilMaciorowski Couldn't make the ! solution to work, but this made me figure out a simpler solution, see edited answer. – xenoid Jan 14 '20 at 20:52
  • @KamilMaciorowski Both of your latest commands fail - I'm guessing this is something to do with the mv command as that's the only thing that's still failing. I first thought {} was in the wrong place but moving it to right after mv then causes a missing argument to exec error. – Hashim Aziz Jan 14 '20 at 23:10
  • 1
    @KamilMaciorowski Works for me... (must have missed something earlier). OP has put the gnu tag so we can assume Gnu coreutils... – xenoid Jan 14 '20 at 23:38

Using find -exec is easy for simple commands. But I think it's better to pipe into | while read var; do ...cmd1 $var; cmd2 "$var"; cmd3;...stuff...; done for more advanced stuff:

find . -maxdepth 1 -type f | while read f; do identify "$f" > /dev/null 2>&1 || mv --backup=numbered "$f" ./Corrupted; done

I finally managed to figure out a version of the shorter commands given by Kamil and xenoid that actually works. I mainly did this by moving the {} to before the -t option, although with the exec... + syntax this also results in the following error:

find: missing argument to `-exec'

I therefore also had to switch to using the exec... \; syntax, which then finally results in the following working command:

find . -maxdepth 1 -type f -iname "*.jpg" ! -exec identify -regard-warnings -verbose > /dev/null {} \; -exec mv {} -t ./Corrupted \;

The -regard-warnings switch to identify is needed to make sure that warnings exit as non-zero in addition to errors (which is necessary to move those corrupted images that only flag warnings), and the -verbose switch is needed to print the warnings and errors to the screen. If you want to see no output at all, you can leave -verbose out.


Try to use parentises:

(find . -maxdepth 1 -type f -exec identify -regard-warnings -verbose {} > /dev/null 2>&1 +) || mv "$1" --backup=numbered {} -t "./Corrupted"   

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.