Size: 3890
Comment: Rewrite
|
← Revision 6 as of 2024-12-28 15:42:20 ⇥
Size: 4519
Comment: Rewrite
|
Deletions are marked like this. | Additions are marked like this. |
Line 21: | Line 21: |
`find(1)` searches a directory for any files that match patterns and/or rules. To search for files by name, try: |
Basic usage: |
Line 26: | Line 24: |
find somedir -name '*.zip' | find . -name '*.zip' |
Line 29: | Line 27: |
The pattern is a glob shell expression, so options available are: | The pattern is a glob shell expression, ''not'' a regular expression pattern. The options available are: |
Line 31: | Line 29: |
+ `?` as a wildcard + `*` as zero or more wildcards + character ranges like `[0-9]` |
* `?` as a wildcard * `*` as zero or more wildcards * character ranges like `[0-9]` |
Line 35: | Line 33: |
It is ''not'' a regular expression pattern. | But to ensure that the glob is not eagerly evaluated by the shell, the pattern must be quoted. |
Line 37: | Line 35: |
Furthermore, note that the pattern is checked against the basename of the file; the directory components of the path are not checked. As a result, the only file that can ever match a pattern that includes a `/` is the root directory itself. | Furthermore, note that the pattern is checked against the basename of the directory entry. The only possible match to a pattern that includes a `/` is the root directory itself. |
Line 41: | Line 39: |
=== Search by File Type === | === Options === |
Line 43: | Line 41: |
To search for only files, try: | ||'''Options''' ||'''Meaning''' ||'''Example''' || ||`-name 'pattern'`||Filter to entries with a basename matching a pattern||`-name '*.zip'` || ||`-type [df]` ||Filter to files or directories ||`-type f` || ||`-user name` ||Filter to entries owned by a user ||`-user root` || ||`-group name` ||Filter to entries owned by a group ||`-group root` || ||`-perm octal` ||Filter to entries with specific permissions ||`-perm 775` || ||`-not` ||Invert a subsequent filter ||`-not -perm 775`|| ||`-mindepth int` ||Only search subdirectories some layers deep ||`-mindepth 1` || ||`-maxdepth int` ||Stop searching subdirectories at some layers deep ||`-maxdepth 1` || `-mindepth` and `-maxdepth` are global options, so must precede any other options. |
Line 46: | Line 54: |
find somedir -name '*.zip' -type f }}} Conversely, use `-type d` to search for only directories. === Search by File Ownership === To search for files by ownership, try: {{{ find somedir -user foo -exec chown bar {} \; }}} To search for files by group, try: {{{ find somedir -group foo -exec chgrp bar {} \; }}} === Search by File Permissions === To search for files by permissions, try: {{{ find somedir -type d -perm 755 }}} Conversely, to search for files with ''any other'' permissions, try: {{{ find somedir -type f -not -perm 644 -exec chmod 644 {} \; |
$ find . -name '*.txt' -maxdepth 1 find: warning: you have specified the global option -maxdepth after the argument -name, but global options are not positional, i.e., -maxdepth affects tests specified before it as well as those specified after it. Please specify global options before other arguments. $ find . -maxdepth 1 -name '*.txt' [trim] |
Line 87: | Line 64: |
`find(1)` can execute a command on all matching files. | `find(1)` can execute a command on all still-matching files. |
Line 89: | Line 66: |
1. The `-exec` flag consumes everything following the flag up to an escaped delimiter, i.e. `\;`. 2. If that string contains a `{}`, that is expanded into the matching filename. * Note that there should only be one instance of `{}` in the string. Behavior for additional instances is undefined, non-portable, and unsupported. 3. The string is then evaluated as a command and executed in a subshell. |
1. The `-exec` option consumes all subsequent arguments up to an escaped delimiter, i.e. `\;`. 2. If one of those arguments contains `{}`, that is expanded into the filename. * Note that there should only be one `{}`. Behavior for additional instances is undefined, non-portable, and unsupported. 3. The arguments are executed in a subshell. |
Line 94: | Line 71: |
4. The return code of the subshell determines if the option is considered a match or non-match The `-exec` option can be used to implement custom filters. For example: {{{ find somedir -name '' -exec grep --fixed-strings '<!--nomin-->' --quiet {} \; }}} The `-exec` option can also be used for scripting, to execute some action on all matches. For example: |
|
Line 101: | Line 87: |
Multiple commands can be chained, i.e. the second command only executes if the first command exits with a success code (0). {{{ find somedir -name '' -exec grep -F '<!--nomin-->' --quiet {} \; -exec cp {} ../production/ \; }}} |
---- |
Line 109: | Line 91: |
=== Programming Tips === | == Tips == |
Line 117: | Line 99: |
Furthermore, any subshells started for `-exec` flags also operate in the working directory. | Furthermore, any subshells started by the `-exec` option operate in the working directory. |
Line 119: | Line 101: |
These details can be inconvenient for whenever commands depend on proximate files, or for when the output will be parsed as text data. | This can be tricky and inconvenient for scripting. Consider: |
Line 121: | Line 103: |
1. Some issues can be alleviated by always calling with an absolute directory. Try: {{{ find "$(realpath somedir)" }}} 2. Other issues can be alleviated by always calling with the current directory. Try: {{{ cd somedir && find . }}} 2. Some implementations support `-execdir`. This flag mirrors `-exec` except that... |
1. Always call with an absolute directory. For example: `find "$(realpath somedir) ...` 2. Change directory instead of passing a directory. For example: `cd somedir && find . ...` 3. Check if your `find(1)` implementation supports the `-execdir` option. This flag mirrors `-exec` except that... |
Line 132: | Line 108: |
* This inconsistency in behavior can lead to subtle issues. Use with caution, and consider adding a `-type f` pattern as well. More generally, this is a code smell; consider redesigning the approach. | `find(1)` should never be piped directly into another command. If the `-exec` option is insufficient, the safe ways to use the output of `find(1)` are: {{{ while IFS= read -r -d '' file; do echo "$file" done < <(find . -name '*.zip' -print0) }}} {{{ find . -name '*.zip' -print0 | xargs -0 echo }}} |
Find
find(1) is a file finder.
Contents
Installation
find(1) will be pre-installed on any Linux or BSD operating system, as a POSIX utility.
Usage
Basic usage:
find . -name '*.zip'
The pattern is a glob shell expression, not a regular expression pattern. The options available are:
? as a wildcard
* as zero or more wildcards
character ranges like [0-9]
But to ensure that the glob is not eagerly evaluated by the shell, the pattern must be quoted.
Furthermore, note that the pattern is checked against the basename of the directory entry. The only possible match to a pattern that includes a / is the root directory itself.
Options
Options |
Meaning |
Example |
-name 'pattern' |
Filter to entries with a basename matching a pattern |
-name '*.zip' |
-type [df] |
Filter to files or directories |
-type f |
-user name |
Filter to entries owned by a user |
-user root |
-group name |
Filter to entries owned by a group |
-group root |
-perm octal |
Filter to entries with specific permissions |
-perm 775 |
-not |
Invert a subsequent filter |
-not -perm 775 |
-mindepth int |
Only search subdirectories some layers deep |
-mindepth 1 |
-maxdepth int |
Stop searching subdirectories at some layers deep |
-maxdepth 1 |
-mindepth and -maxdepth are global options, so must precede any other options.
$ find . -name '*.txt' -maxdepth 1 find: warning: you have specified the global option -maxdepth after the argument -name, but global options are not positional, i.e., -maxdepth affects tests specified before it as well as those specified after it. Please specify global options before other arguments. $ find . -maxdepth 1 -name '*.txt' [trim]
Command Execution
find(1) can execute a command on all still-matching files.
The -exec option consumes all subsequent arguments up to an escaped delimiter, i.e. \;.
If one of those arguments contains {}, that is expanded into the filename.
Note that there should only be one {}. Behavior for additional instances is undefined, non-portable, and unsupported.
- The arguments are executed in a subshell.
- Note then that any environmental variables and functions are not shared.
- The return code of the subshell determines if the option is considered a match or non-match
The -exec option can be used to implement custom filters. For example:
find somedir -name '' -exec grep --fixed-strings '<!--nomin-->' --quiet {} \;
The -exec option can also be used for scripting, to execute some action on all matches. For example:
Try:
find somedir -name '*.min.js' -exec cp {} ../production/ \;
Tips
find(1) operates from the working directory and prints filenames accordingly. That is...
if called like find ., all filenames will be printed starting with ./.
if called like find .., all filenames will be printed starting with ../.
if called with an absolute directory like find /etc, all filenames will be printed as absolute, too.
Furthermore, any subshells started by the -exec option operate in the working directory.
This can be tricky and inconvenient for scripting. Consider:
Always call with an absolute directory. For example: find "$(realpath somedir) ...
Change directory instead of passing a directory. For example: cd somedir && find . ...
Check if your find(1) implementation supports the -execdir option. This flag mirrors -exec except that...
- if a file is matched, the execution occurs in the same directory as that file.
- if a directory is matched, the execution occurs in the parent directory.
find(1) should never be piped directly into another command. If the -exec option is insufficient, the safe ways to use the output of find(1) are:
while IFS= read -r -d '' file; do echo "$file" done < <(find . -name '*.zip' -print0)
find . -name '*.zip' -print0 | xargs -0 echo