Find

From HalfgeekKB
Revision as of 12:04, 9 January 2017 by Psmay (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Template:LowercaseTemplate:Unix

find is a unix program that lists files found recursively under a path.

Null-terminated output

See null-terminated (console).

Unsorted notes

Until I have time to properly document this, mess with it. Try some of the following:

# Lists files in and below your home directory
find ~/

If the path to be searched is ".", that parameter can usually be omitted.

# Lists files in and below the current directory
find .
# or
find
# Lists only the regular files (no dirs, symlinks, pipes, and so on)
find . -type f
# Lists only the dirs
find . -type d
# Lists regular files and symlinks
find . -type f -o -type l
# or, as a component to something more complex,
find . \( -type f -o -type l \)

To run a command on each path found, use -exec. This example runs md5sum for all regular files (not symlinks, not directories, ...) in and below the current directory, if the read permissions are okay:

find . -type f -exec md5sum {} \; > ../MD5SUMS

The list of files can also be piped to xargs or perl; if using either one, use the -print0 condition on find to use null bytes as delimiters, then use the -0 switch on xargs or perl to expect the same.

# Use \0 as a delimiter instead of \n
find . \( -type f -o -type l \) -print0 | perl -n -0 -E 'dosomethingwith($_)'

Using -exec as a for-each mechanism

The predicate -exec runs a command for each filename found. When in use, the -print predicate is no longer implied. -exec is used as such:

find ... -exec command [someargs] {} [otherargs] ';' ...

For every filename filename found, the command

command [someargs] filename [otherargs]

would be run. The {} and the ';' are not optional, while [someargs] and [otherargs] are optional.

Note that when passing arguments to an inline sh/bash script an extra argument should be used to fill the $0 argument if "$@" is being used:

find ... -exec sh -c 'echo "$@"' "$0" {} ';'

find and replace

Using sed:

find ... -exec sed -i.orig 's/PATTERN/REPLACEMENT/g' {} ';'

Using perl:

find ... -exec perl -p -i.orig -e 's/PATTERN/REPLACEMENT/g' {} ';'

Replace -i.orig with -i to suppress backups while still changing files in place.

dos2unix/unix2dos

find ... -exec dos2unix {} ';'
find ... -exec unix2dos {} ';'

Call a shell function (bash only)

In bash (but not plain sh) we can export a bash function with export -f and use it with -exec via a special invocation of bash:

#!/bin/bash

# Define a function
somefunc() {
	...
}

# Export the function to subshells
export -f somefunc

# Call from find
find ... -exec bash -c 'somefunc "$0"' {} ';'

Output relative URIs

Perl's URI::file knows how to convert a local file to a properly escaped URI.

find '...' -print0 | perl -0 -MURI::file -n -E 'chomp; say "".URI::file->new($_)'

Sorting by mtime

find won't sort by mtime by itself. This general command produces a sorted result that should be acceptable by anything that would accept the result of `find … -print0`:

# Let $DESTZ be a command that accepts a list of null-terminated filenames
find … -printf '%T@ %p\0' | sort -zk 1n[r] | sed -z 's/^[^ ]* //' | $DESTZ

such as:

find -type f -printf '%T@ %p\0' | sort -zk 1n | sed -z 's/^[^ ]* //' | xargs -0 -i echo 'File: {}'

Breakdown:

# Note that the intermediate processing steps use \0-terminated lists in
# order not to lose information about oddly named files.
find … -printf '%T@ %p\0' | # Prefixes all files with their numeric mtime and a space
sort -zk 1n[r] | # Sorts lines numerically by the prefixes (use `1n` for old to new or `1nr` for new to old)
sed -z 's/^[^ ]* //' | # Strips the prefixes from the sorted result
$DESTZ # Accepts list of null-terminated filenames

Combining with Perl on the command line

Use perl's -0 switch to allow -print0 to be treated as lines.

This line prints file modified times ((stat(file))[9] gets the mtime):

find -print0 | perl -n0e '$m=(stat("$_"))[9]; printf(q(%12s %s)."\n",$m,$_)'

You may also find my except-most-recent script handy if you find yourself writing a lot of backup rotation scripts.

See also

  • xargs, which turns the output of this program into command line arguments.