Your command is first parsed by the shell into two commands separated by a ;
, which is equivalent to a newline:
find . -name "*" -exec chgrp -v new_group {}
chmod -v 770 {} \;
If you want to run a shell command, invoke a shell explicitly with bash -c
(or sh -c
if you don't care that the shell is specifically bash):
find . -name "*" -exec sh -c 'chgrp -v new_group "$0"; chmod -v 770 "$0"' {} \;
Note the use of {}
as an argument to the shell; it's the zeroth argument (which is normally the name of the shell or script, but this doesn't matter here), hence referenced as "$0"
.
You can pass multiple file names to the shell at a time and make the shell iterate through them, it'll be faster. Here I pass _
as the script name and the following arguments are file names, which for x
(a shortcut for for x in "$@"
) iterates over.
find . -name "*" -exec sh -c 'for x; do chgrp -v new_group "$x"; chmod -v 770 "$x"; done' _ {} +
Note that since bash 4, or in zsh, you don't need find at all here. In bash, run shopt -s globstar
(put it in your ~/.bashrc
) to activate **/
standing for a recursive directory glob. (In zsh, this is active all the time.) Then
chgrp -v new_group -- **/*; chmod -v 770 -- **/*
or if you want the files to be iterated on in order
for x in **/*; do
chgrp -v new_group -- "$x"
chmod -v 770 -- "$x"
done
One difference with the find
command is that the shell ignores dot files (files whose name begins with a .
). To include them, in bash, first set GLOBIGNORE=.:..
; in zsh, use **/*(D)
as the glob pattern.
-name "*"
matches every name, so it's a no-op test that can be removed. Also, bothchmod
andchgrp
has a-R
option for recursive operation. – Kusalananda Dec 18 '20 at 20:20