Act on all files in a directory tree using find
Tested on |
Debian (Etch, Lenny, Squeeze) |
Fedora (14) |
Ubuntu (Hardy, Intrepid, Jaunty, Karmic, Lucid, Maverick, Natty, Precise, Trusty) |
Objective
To perform a given action on all files in a given directory tree
Scenario
The path ~/src/foo-1.0.0/
is the root of a directory tree which contains files of several different types, including a number of Perl scripts. You wish to ensure that these scripts have a file mode of 755 (writable by their owner, readable or executable by anyone). The scripts may be distinguished from the other files by the fact that their names end in the suffix .pl
.
Method
List the files to be acted upon using find
. Use the -execdir
option to execute the required command:
find ~/src/foo-1.0.0 -name "*.pl" -type f -execdir chmod 755 {} \;
The first argument to find
is the root of the directory tree to be searched.
The option -name
precedes a glob pattern which in this case restricts the search to files with the extension .pl
. The pattern must be placed in quotes, otherwise there is a risk that it will be expanded by the shell before being passed to find
.
The option -type f
specifies that only regular files are to be matched (as opposed to, for example, directories). By default find
will match everything that it finds in the given directory tree, but this is rarely the desired behaviour when passing the pathnames to a command.
The option -execdir
specifies the command to be executed. The symbol {}
acts as a placeholder for the pathname to be acted upon (and may be used more than once if necessary). The components of the command must be passed as separate arguments, not as a monolithic string. The semicolon (which should normally be escaped, as above) terminates the command.
Testing
Commands of the type described here can damage a large number of files in a very short space of time if you make a mistake. You may therefore wish to perform a dry run first, to inspect the commands that would be executed without actually executing them. This can be done by inserting an echo
command after the -execdir
option:
find ~/src/foo-1.0.0 -name "*.pl" -type f -execdir echo chmod 755 {} \;
This technique will not work if the command contains any redirections, but you can work around that limitation by temporarily replacing the redirection token with some other string that has no special meaning to the shell.
It may be helpful to use the pwd
to display the directory in which each command will be executed. This requires some trickery to prevent pwd
from being executed prematurely:
find ~/src/foo-1.0.0 -name "*.pl" -type f -execdir sh -c "echo \`pwd\` chmod 755 {}" \;
(Note that spaces and other special characters will not be escaped in the commands as listed.)
Troubleshooting
An effective strategy for troubleshooting is to first check that the correct pathnames are being selected, then that the correct commands are being executed.
To check which pathnames are selected, execute the find
command without the -execdir
option:
find ~/src/foo-1.0.0 -name "*.pl" -type f
An easy mistake to make here is not putting the patten in quotation marks. If you don't, and there are one or more files in the current working directory that match the pattern, then the pattern will be expanded by the shell. If the pattern expands to exactly one pathname then the find
command will appear to work, but will not necessarily have acted upon all the files it should have done.
Note that long options to find
being with a single hyphen (not the double hyphen required for most other commands).
If the pathnames are correct then the error must lie in the commands that are executed. These can be inspected using the dry-run technique described above. Points to check are that:
- the command is being run with an appropriate
PATH
, and that - the command will tolerate the current working directory being changed to the subdirectory that contains the file.
See also
Tags: shell