Search code examples
unixgnu-findutilsfind-util

Using bash shell on RHEL 7 - why does find find more files when I don't use -exec?


I'm looking for all setuid/setgid files. When I don't use -exec, it works as expected:

# find /usr/bin -type f -perm -4000 -o -perm -2000 
/usr/bin/wall
/usr/bin/ksu
/usr/bin/chage
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/fusermount
/usr/bin/passwd
/usr/bin/write
/usr/bin/su
/usr/bin/umount
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/mount
/usr/bin/pkexec
/usr/bin/crontab
/usr/bin/cgclassify
/usr/bin/cgexec
/usr/bin/ssh-agent
/usr/bin/Xorg
/usr/bin/at
/usr/bin/sudo
/usr/bin/locate
/usr/bin/staprun

When I use -exec, I only get a subset of the results back:

# find /usr/bin -type f -perm -4000 -o -perm -2000 -exec ls -l {} \;
-r-xr-sr-x. 1 root tty 15344 Jan 27  2014 /usr/bin/wall
-rwxr-sr-x. 1 root tty 19536 Aug 21  2015 /usr/bin/write
-rwxr-sr-x. 1 root cgred 15624 Sep 21  2014 /usr/bin/cgclassify
-rwxr-sr-x. 1 root cgred 15584 Sep 21  2014 /usr/bin/cgexec
---x--s--x. 1 root nobody 306304 Sep 24  2015 /usr/bin/ssh-agent
-rwx--s--x. 1 root slocate 40504 Jan 26  2014 /usr/bin/locate

Why?


Solution

  • You're only using -exec on the right-hand side of the -o. Thus, it's being parsed like this:

    # What's actually happening
    find /usr/bin '(' -type f -perm -4000 ')' -o '(' -perm -2000 -exec ls -l {} \; ')'
    

    Obviously, that's not what you want.


    To make it apply to both sides of the conditional, add some parens for grouping:

    # What you want to happen
    find /usr/bin -type f '(' -perm -4000 -o -perm -2000 ')' -exec ls -l {} +
    

    The reason is that if you don't specify an explicit action, find assumes a -print as the default action. When you add an action yourself it turns off that default, so only the items for which you explicitly specify an action get one.

    That is to say:

    # These commands are all equivalent:
    find /usr/bin -type f -perm -4000 -o -perm -2000
    find /usr/bin '(' -type f -perm -4000 -o -perm -2000 ')' -print
    find /usr/bin '(' '(' -type f -perm -4000 ')' -o '(' -perm -2000 ')' ')' -print
    

    Note that last one, which exposes a caveat in the default behavior: You probably wanted the -type f to apply to both sides of the -o, but without explicit grouping it gets put on the left, just as an explicit -exec or print gets put on the right.

    Moral of this story: When using -o in find, be explicit about your grouping unless you're very certain that the default behavior is what you want.