0

I am trying to rename a bunch of images in a directory according to their dimensions. To that end, I am building up my command step by step, using of Imagemagik's identify command. I currently am using echo and plan to switch that to mv once echo outputs correctly.

find * -type f -exec echo "$(identify -format '%w-%h' {})" \;

This outputs a bunch of empty lines. However, when I just run it without the echo directly,

find * -type f -exec identify -format '%w-%h' {} \;

It outputs the dimensions as expected. Why isnt the inner command executing properly?

2
  • Why would you want to do a command substitution and then just echo?
    – Kusalananda
    Commented Sep 25, 2017 at 20:48
  • 1
    Once I get it to echo, Im going to swap that out with a mv in order to rename the file.
    – Jeff
    Commented Oct 11, 2017 at 14:57

2 Answers 2

2

That's because echo doesn't know what is {} inside itself to expand it, only exec understand {}, in this case you should use in sh -c mode.

find . -type f -exec sh -c 'echo "$(identify -format '%w-%h' "$1")"' sh '{}' \;
1

Use:

find . -type f -exec sh -c 'echo "$(identify -format %w-%h "$1")"' sh {} \;

Explanation

The problem is with the way bash expands variables

find * -type f -exec echo "$(identify -format '%w-%h' {})" \;

First bash expands what is in side $(...):

find * -type f -exec echo 'identify: unable to open image `{}': No such file or directory...' \;

At which point find will use the same exec statement for all commands with a rather useless argument. What you want to do is suppress the bash expansion for inside the finds exec. You can stop bash expanding things by placing them inside single quotes

find * -type f -exec echo '$(identify -format %w-%h {})' \;

But here we lose expansion completely, we can inject it back by running the expression with sh -c

find * -type f -exec sh -c 'echo "$(identify -format %w-%h {})"' \;

Having {} within the shell script can also lead to some unexpected errors when dealing with whitespace or quotes or any character special to the shell in file names. To get around this you should move the {} as an argument to sh with:

find * -type f -exec sh -c 'echo "$(identify -format %w-%h "$1")"' sh {} \;

See this question/answer for a more detailed explanation of this.

Finally you can replace the * with . to let find find the files in the current directory by itself (and include hidden ones and not fail for filenames starting with -).

Additionally, you can add the -x flag to sh to get it to print out what was it executed to help with debugging the command - this can be very noisy for a lot of files.

2
  • 1
    See unix.stackexchange.com/a/156010/116858
    – Kusalananda
    Commented Sep 25, 2017 at 18:04
  • The identify: unable to open image... error will be output by identify on its stderr, so won't be captured by $(...). as there won't be anything on stdout, $(...) will expand to an empty string, so it's like running find ... -exec echo '' \; Commented Sep 25, 2017 at 19:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .