-> 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 ?
-> 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.
bash -c
worksOne 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
.
It looks like you've got things reversed from what you want. Try this:
for f in `find .. -name bin`
do
echo $f
done
find
therefore it outputs founded elements like loop by itself
– Costas
Apr 01 '15 at 18:28
$file
is never set to anything. Perhaps you don’t understand what James was trying to do.
– Scott - Слава Україні
Apr 01 '15 at 18:29
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 echo
d - 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 -exec
ing 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' -- {} +
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 02 '15 at 06:09