14

I think I am fairly advanced in my use of find but EVERY time I use it I cannot for the life of me remember the method to close the -exec option. I spend a good deal of time reading every time I use it. Am I simply not using it enough or expecting too much of myself? Lets start with a typical example that gets me frustrated.

Directory structure has files with all incorrect permissions hidden files symbolic links etc. I want to change the ownership to a reasonable value

find . -type f -exec chown username {} \;
find . -type d -exec chown username {} \;
find . -type d -exec chgrp usergroup {} \;
find . -type f -exec chgrp usergroup {} \;

(Forgive me if the ending is backwards... I looked at it an hour ago and still I am not sure)

But I am scared to run it because of mounts, symbolic links, etc. I have made the ultimate goof of chmod .* and had it recurse upwards on me before. I know -xdev will forgo crossing partitions but I am not sure what will happen to the files living inside directories which are symbolic links.

So how does one master this beast that can kill crucial files?

Update pruning the best suggestions below and summarizing:

  1. Have a practice directory linked and mounted to other practice directories.
  2. Use xargs rather than the non-intuitive exec command.
  3. Use -exec echo {} for sanity and safety
  4. Semi colon is special and you are escaping it therefore the escape char is first
  5. The -or command can help you combine selection criteria.

I am a little confused about the print0 but xargs has always been a bit on the not easy to understand at first glance practice that I try and avoid.

3
  • 1
    I can't remember it either, so I just pipe it into xargs.
    – resonator
    Jun 22, 2009 at 0:35
  • 2
    When you are satisfied with building the right command, consider running it with + instead of \; It'll run faster with + because the exec command will be called a lot less often.
    – wzzrd
    Jun 22, 2009 at 6:09
  • wzzrd, great tip about using + rather than \; Jun 22, 2009 at 9:52

9 Answers 9

20

Well, as far as the -exec syntax goes, you could do like a lot of people, give up and use xargs:

find . -type f | xargs chown username

(or the files-with-spaces-and-other-nonsense-in-them-safe version)

find . -type f -print0 | xargs -0 chown username

Or, to try to remember the right thing to do with the semicolon, what you need to drill into your head is that you're using a semicolon to terminate the command that -exec is running, and you have to escape the semicolon because it has special meaning to bash. Which is why it's backslash semicolon. You seem to have the {} substitution part okay.

As to killing files and so on, if you're running something big and dangerous like you're talking about, first do this:

find . -type f -exec echo chown username {} \;

and review the results. This is basically a "dry run" where you're seeing the commands it would run if you let it. Definitely a good practice. Won't help with the .* problem, but you know not to do that one now. :)

1
  • 1
    +! for the 'echo' practice dry run. I always use this for a big change Jun 22, 2009 at 5:21
3

I'm surprised nobody has yet mentioned the following option:

find . -type f -ok chown username {} \;

This syntax will confirm the command before executing it. It works best if you expect your match to be a relatively small number of files because it will prompt for each one.

Some answers mention xargs, but with GNU find that too is unnecessary:

find . -type f -exec chown username {} \+

Note that the + typically doesn't need to be escaped, but it's good to get in to the habit of doing so anyway.

2
  • Can you explain the nature of the plus sign... what exactly is it doing? Is the shell processing it or the command. I think I am going to download the source code for gnu-find because this is an interesting topic.
    – ojblass
    Jun 29, 2009 at 17:40
  • find is processing it. Read man find to find out more about that option. Jun 29, 2009 at 17:52
3

This is what man is for: It can be hard to remember things, that is why *nix command includes man pages. You can just always check the ACTION section of the man page for a reminder on the syntax: man find. Although man pages may seem not to friendly, once you get good at reading them they are very handy.

Make a Front-End or Wrapper: When I wanted to get better at GNU find I wrote a GUI front end for find that generates find commands with python, glade, and pygtk. This is a good exercise if want to get to know it really well.

Have a Practice Directory: Lastly, I always have a 'scrap' directory in my home dir for playing with powerful commands when something like 'echo' doesn't cut it. You can use shell expansion to quickly make a bunch of files with something like:

for i in {1..100}; do touch "$i"; done
1
  • Strange a practice directory is something I have never considered.
    – ojblass
    Jun 23, 2009 at 2:30
1

You can also combine find expressions with '-and' and '-or' operators. -and is implicit:

find . -type f -name "plainfile" ...

is the same as

find . -type f -and -name "plainfile" ...

The or operator can reduce your list of 4 commands to 2:

find . -type d -or -type f ...

And finally, not related to find, but chown can also set the group as well. So, coping with spaces in names as well we end up with:

find . -type d -or -type f -exec chown username:usergroup '{}' \;
2
  • What find with the -or business. [Sic]
    – ojblass
    Jun 29, 2009 at 17:39
  • @ojblass: find combines expressions using 'and' or 'or', by default if neither '-and' nor '-or' are specified, '-and' is used implicitly. So type 'find -type d -type f' will not find anything because nothing is a directory and a file. Jun 30, 2009 at 8:07
0

When closing I just think of using ; like with many programing languages and then escaping it. Just remember that is needs to be escaped and it comes to you pretty easily.

0

Depending on the version of find that you are using there are different options that may help you. GNU find is probably the most powerful and this may help you with your second (implied) question

For instance -- the default behaviour is not to dereference symbolic links, but you can override that; and the -mount option stops find going across mount points

Look in your local man pages

0

Some would argue that the xargs option is faster than the -exec option because it won't execute the command once for each file, but I wouldn't worry about that for anything except massive jobs taking a non trivial amount of time.

Personally, I usually just run find without exec

find /path/to/dir -name whatever

make sure that the output looks correct, and then run my command on that list.

chown user:group `find /path/to/dir -name whatever`
2
  • Perfect example of how "not" to do things. Filenames with spaces. Jun 22, 2009 at 2:28
  • Yep, that's a potential problem which I should perhaps have acknowledged. However my point was about reading the find output before executing anything, where you would spot spaces or other file name madness. Jun 22, 2009 at 2:42
0

For some shells (not bash) escape {} for proper behavior.

\{\} or '{}'

find manual is good for mastering find.

2
  • No you don't; find runs just fine with the {} as they are. They are part of the find command: they are a token that gets replaced by the result of the find command. They only mean something in bash when defining functions or calling variables, so you can just use them as in the original question.
    – wzzrd
    Jun 22, 2009 at 6:07
  • Its from the man page. I updated to say its not applicable to bash. Jun 22, 2009 at 6:23
0

Before you do anything like this make sure the directory your running this command against isn't in the same filesystem as /usr or /etc. (otherwise if I have a hard link to /etc/passwd or to /bin/sh in my home directory you've just given me ownership of those and as a side-effect I now own the machine).

If you want to recurse through a directory and chown all the files/directories, a simple

chown -Rh user:usergroup user/

will do it. Make sure you use the -h or chown will follow symbolic links.

3
  • I don't think this is cross platform. Also fails with 247K of files as demonstrated by my practice environment. I am not sure why.
    – ojblass
    Jun 24, 2009 at 1:26
  • I think you've found a bug more than you've found a crossplatform issue. Which version and what's the exit status when it fails? As far as I can tell it first originated in early bsd versions; it's definitely in sunos 5.3 and ultrix and irix. How many does it process before it fails?
    – chris
    Jun 24, 2009 at 3:52
  • You are right it works on AIX 5.3, AIX 5.2, but not AIX 4.3, NCR, HP 11, HP 10.20, or my DSL distributions. IN all cases it appears to fail at or near 60k files.
    – ojblass
    Jun 29, 2009 at 17:36

You must log in to answer this question.

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