I'm trying to build a find command with multiple conditions that lead to multiple actions. It was suggested to me that I should use only one find command for efficiency. Here is what I have:

    find $target \
            \( ! -group $project -exec chgrp $project "{}" \;       \) , \
            \( ! -user $owner -exec chown $owner "{}" \;            \) , \
            \( ! -perm "$perms" -exec chmod "$perms" "{}" \;        \) , \
            \( -type d -exec chmod g+s "{}" \; \)

It seems to actually do something, however I get:

find: invalid expression


(: command not found

in the script I am trying to execute

  • 3
    Wow... Is this really worth it? Why not a simple chmod -R, chown -R etc?
    – terdon
    Sep 13, 2013 at 21:48
  • Yes, it's worth it. Dealing with a lot of files here, and a flat chmod/chown -R takes significantly longer--like the better part of a day. I don't know why. Maybe find only has to stat each file once for all 4 conditions when combining them this way. But it's certainly a lot faster on the filesystem we're using.
    – Angelo
    Sep 13, 2013 at 22:35
  • I get a different error when copy pasting, could you give us an minimal non-working example using actual values instead of variables? It might be the variables that are complicating things.
    – terdon
    Sep 13, 2013 at 22:39
  • You're probably right, I'm unable to reproduce the error when I remove the line with the $perms variable. But even if I comment that line out, I still have an error, so I'm not sure what it is. perms is ug+rX,go-w,o-rx
    – Angelo
    Sep 13, 2013 at 22:52
  • Hang on, sorry I had not read your comment carefully enough. If you still have an error, please edit your question using the actual strings involved and we'll see if we can help.
    – terdon
    Sep 13, 2013 at 23:47

1 Answer 1


The error message indicates that ( was executed as a command, which means that one of the backslashes used for line continuation are in fact not the last character of the line. Make sure that there is no whitespace. Make sure that you're using Unix line endings (LF only, no CR).

The complaint from find about an invalid expression is due to those commas. Just remove them.

find "$target" \
        \( ! -group "$project" -exec chgrp "$project" {} \;   \) \
        \( ! -user "$owner" -exec chown "$owner" {} \;        \) \
        \( ! -perm "$perms" -exec chmod "$perms" {} \;        \) \
        \( -type d -exec chmod g+s {} \; \)

You will probably save time running the commands once per batch of files instead of once per file. This is not guaranteed here because the chown, chgrp and chmod calls may proceed at different rates so directory entries may be evicted from the cache, but I'd give it a go.

find "$target" \
        \( ! -group "$project" -exec chgrp "$project" {} +   \) \
        \( ! -user "$owner" -exec chown "$owner" {} +        \) \
        \( ! -perm "$perms" -exec chmod "$perms" {} +        \) \
        \( -type d -exec chmod g+s {} + \)

chgrp, chmod and chown do nothing if the file already has the right metadata, so you could call them unconditionally. Running them needlessly does cause more calls, however. The utilities will call stat again after find has done so, but the inodes are highly likely to still be in the cache, so this may be worthwhile. You can save a call by combining chgrp into chown.

find "$target" -exec chown "$owner:$project" {} + \
        -exec chmod "$perms" {} + \
        -type d -exec chmod g+s {} +
  • Will this still be faster if you remove the not conditions from find? I had assumed the speed-up was because the chmod etc commands were only run if necessary. In your last example, find does not check for that so won't it be equivalent to chown -R foo:bar;chmod -R $perms; chmod g+s? Apart from the last of course that does limit it to directories only.
    – terdon
    Sep 14, 2013 at 0:25
  • @terdon The relative performance of the last command depends a lot on how many files need to be modified, so I recommend that Angelo tries both with his data. Using find instead of chown -R …; chmod -R … makes a difference because the data is read from the disk only once: after find has called stat, it's still in the cache for chown` and chmod. Sep 14, 2013 at 0:33
  • Of course, should have realized that if find is checking the files it will need to read the data so having the conditionals won't make much difference. I see, thanks.
    – terdon
    Sep 14, 2013 at 0:48
  • Thanks, having extra whitespace at the end of a line seems to explain what I was experiencing.
    – Angelo
    Sep 14, 2013 at 2:01
  • 1
    @Angelo find … -exec somecommand {} + executes somecommand with multiple matching files passed as separate arguments (like xargs does). find … -exec somecommand {} \; executes somecommand once per matching file. See the find documentation for more information. Sep 18, 2013 at 17:20

You must log in to answer this question.

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