1

When I run this command in Terminal:

find . -type f -name "*.png" -exec sh -c "file {} | egrep -o '^.*\d+,'" \;

I get this error if a filename contains parentheses:

sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `file ./(terrible filename).png | egrep -o '^.*\d+,''

I know it has something to do with the sh -c, but I don't know how to fix it, thanks.

./(terrible filename).png: PNG image data, 512 x 512, // trying to get this result

Jens
  • 69,818
  • 15
  • 125
  • 179
Cocoa Puffs
  • 724
  • 2
  • 8
  • 16

2 Answers2

4

You are basically pasting the file name into sh -c '...' without any quoting. The string inside sh -c after the substitutions made by find needs to be valid sh syntax, which means there can be no unquoted single quotes, parentheses, etc.

A more robust approach is to use -exec file {} and pass all the output from find to egrep.

find . -type f -name "*.png" -exec file {} \; | egrep -o '^.*\d+,'

The placeholder token {} gets replaced by find with the filename currently being processed. When it is a lone token, find can pass in any file name at all; but if you interpolate it into a longer string, such as a shell command, you will need to ensure that any necessary quoting etc. is added somehow. That's messy, so usually you will want to find a solution where you don't need to do that.

(As pointed out in comments to the other answer, -exec sh -c 'file "$1"' _ {} \; is another way to accomplish that; this generalizes to arbitrarily complex shell commands. If your find supports exec {} \+ you want to add a simple loop: -exec sh 'for f; do file "$f"; done' _ {} \+ -- incidentally, the _ is a dummy placeholder for $0.)

Community
  • 1
  • 1
tripleee
  • 175,061
  • 34
  • 275
  • 318
  • I tried something similar earlier, but I got the `{} \; |` part wrong where you got it right - thank you! one quick question - what is the difference between using `{} +;` and `{} \;` – Cocoa Puffs Dec 29 '15 at 10:32
  • The former is more efficient but not supported everywhere; it is a fairly recent addition (by glacial Debian / OSX standards). – tripleee Dec 29 '15 at 10:57
0

Are there parentheses in the file names? This might help:

find . -type f -name "*.png" -exec sh -c "file '{}' | egrep -o '^.*\d+,'" \;
Jens
  • 69,818
  • 15
  • 125
  • 179
  • facepalm! it's always the small things! thank you! :D – Cocoa Puffs Dec 29 '15 at 10:14
  • 2
    Still not safe against filenames with single quotes. Use `-exec sh -c 'file "$1" | blah blah' -- {}` instead. – 4ae1e1 Dec 29 '15 at 10:14
  • @4ae1e1 Thanks, this is a valuable hint! – Jens Dec 29 '15 at 10:15
  • @4ae1e1: I get another error when i use it that way... `file -C -m magicfiles, Try \`file --help' for more information.` – Cocoa Puffs Dec 29 '15 at 10:16
  • @4ae1e1: Thanks, I just saw the update; No error this time, but it didn't find the file either... – Cocoa Puffs Dec 29 '15 at 10:18
  • @CocoaPuffs I was typing without thinking... Anyway, the idea is there. Not sure why you're getting the strange error. What if you just do `sh -c 'file "$1" | blah blah' -- insert_terrible_name_here`? – 4ae1e1 Dec 29 '15 at 10:20
  • @4ae1e1: This didn't work: `echo "(terrible filename).png" | sh -c 'file "$1" | egrep "^.*\d+,"'`, but this does `sh -c 'file "(terrible filename).png" | egrep "^.*\d+,"`'. Not sure why it doesn't like the `$1`. – Cocoa Puffs Dec 29 '15 at 10:28
  • @CocoaPuffs You're doing it wrong. `$1` is the first positional argument, while you're passing the filename through stdin (`$1` is empty in your case). – 4ae1e1 Dec 29 '15 at 10:30
  • @4ae1e1: even if I do `fnfo=$"(terrible filename).png"` and then `sh -c 'file "$fnfo"'` the result comes up empty... no error though. – Cocoa Puffs Dec 29 '15 at 10:35
  • 1
    @CocoaPuffs Of course it would behave that way. `fnfo` isn't exported, so `sh` doesn't see it. But why are you doing this? `find . -type f -name "*.png" -exec sh -c 'file "$1" | egrep -o "^.*\\d+,"' -- {}` still doesn't work? (I mean, at this point, the discussion is just theoretical; tripleee's answer is better, and my comment was supposed to fix this answer — not to point out the best solution — to begin with). – 4ae1e1 Dec 29 '15 at 10:41
  • @4ae1e1: ^ that works! thanks for clarifying! one too many somethings for this blonde :) the parts where i'm a bit hazy are the differences in `{} +;`, `{} \;`, `-- {} \;`... somehow i have no idea what those do exactly (terminators i presume) :/ – Cocoa Puffs Dec 29 '15 at 10:43
  • @4ae1e1: I will try to locate the missing manual for those, since that's another question itself. Anyway, thanks for the enlightenment -- always appreciated {} \; - feliz navidad! :) – Cocoa Puffs Dec 29 '15 at 10:54
  • @CocoaPuffs http://linux.die.net/man/1/find. `-- {} \;` isn't a special construct though, I'm just using the standard `-exec command \;`; the `--` is for `sh`, or `{}` will be `$0`, and it's my habit to not use `$0` as it has an obvious special meaning but instead use `--` as a placeholder for `$0` (won't call it reasonable — it's just a personal habit). See http://linux.die.net/man/1/sh. – 4ae1e1 Dec 29 '15 at 11:05