-> find  .. -name bin  -exec  for file in {}/* ; do echo $file ; done  \;

   -bash: syntax error near unexpected token `do'

What is the proper syntax for this command ?


To use multiple statements, such as a for-loop, as the argument to -exec, one needs to invoke a shell, such as bash, explicitly:

find .. -name bin -exec bash -c 'for file in "$1"/* ; do echo "$file" ; done' none {}  \;

This is safe even for filenames that contain spaces or other hostile characters.

How bash -c works

One can invoke bash with a command of the form:

bash -c some_complex_commands arg0 arg1 arg2 ...

In this case, bash will execute whatever is in the string some_complex_commands. Those commands can make use of the usual shell positional parameters. The first argument after command, arg0 above, is assigned to $0, the second to $1, the third to $2, etc.

When one executes a normal shell script, $0 is the name of the script and $1 is the first argument that appears on the command line. In keeping with that tradition, the bash -c command was written to assign the file name, {} in find's notation, to $1. Since this script does not have a sensible name, none is assigned as a placeholder to $0.

  • ya man bash those hostile characters – Arcabard Apr 1 '15 at 18:21

You can approximate the output of your pseudo-code there with find primitives as is:

find .. -path \*/bin/\* ! -name .\* 

...which should print only files/dirs with names that do not begin with a . and which are rooted at some level both in the parent directory, and, at some greater degree, in a dir named /bin.

In general I can think of all kinds of practical purposes for looping in a find -exec child process, but not a one in which I could consider it practical to do shelld glob on an argument passed by find. To do the same thing your pseudo-code does with printf - because its use can guarantee literal translation of arguments to output - you might do...

find .. -type d -name bin -exec sh -c '
    printf %s\\n "$0/"*' {} \;

...which does the printing and the globbing without the for loop. One difference between this and your example command, though, is that without the -type d specification and type of result matching the name bin will be echod - and so you're highly likely to see a lot of bin/* being written to stdout. Of course, even w/ -type d, there's no guarantee that the * will resolve - an empty directory or one containing only .files will render no matches and so you might see it anyway.

Note also that the example pseudo code, because it uses the {} \; primitives might be a lot slower than some other ways. We'll try the printf thing again like:

find .. -type d -name bin -exec sh -c '
    for d do printf %s\\n "$d/"*; done' -- {} +

...which still risks the empty glob case but instead of -execing a shell per match, it rather gathers as many arguments as it might reasonably pass off to another process at exec time and passes the lot to sh in "$@" its positional parameter array - which we can loop over with for d do printf....

Now if you wanted to do something other than just print results - which is what I would typically consider useful about looping in an -exec statement - you can fallback to the earlier -path example and combine it with -exec like...

find .. -path \*/bin/\* -exec sh -c '
    for arg do : something with "$arg"
done' -- {} +
  • This is very useful . While waiting for an answer I actually figured to use globstar . I was actually going to ask about adapting my find command to find more specifically the files in bin and not just the bins to recurse . – Arcabard Apr 2 '15 at 5:25
  • @JamesAndino - You can do it without recursion: find .. -path '*/bin/*' ! -name . -prune ! -type d which should print only files other than type directory which reside in a directory named ../(*/)*bin. – mikeserv Apr 2 '15 at 6:09

It looks like you've got things reversed from what you want. Try this:

for f in `find .. -name bin`
echo $f
  • Nope . I want to know the syntax for what I placed in the original question . – Arcabard Apr 1 '15 at 17:55
  • @JamesAndino There is no need to loop inside find therefore it outputs founded elements like loop by itself – Costas Apr 1 '15 at 18:28
  • 1
    Your answer doesn’t even do anything except for printing a bunch of blank lines — $file is never set to anything.  Perhaps you don’t understand what James was trying to do. – Scott Apr 1 '15 at 18:29
  • Correct on both counts, though I've just fixed the answer to address the first issue you noted. I still don't understand why he's looping inside the find's exec instead of looping over the entire output, but that may be because he never explained that. – John Apr 1 '15 at 18:36
  • I'm finding a set of folders then looping through the collection of commands in those bins . The problem with your answer is that if there are new line characters ( maybe spaces tabs etc I always forget ) the script will die a fiery death . Normally not a problem looping your own bin but I'll look for sub folders with a user chosen name and kaboom goes trash puter – Arcabard Apr 2 '15 at 5:22

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.