diff options
author | David Peter <mail@david-peter.de> | 2021-11-26 18:18:25 +0100 |
---|---|---|
committer | David Peter <mail@david-peter.de> | 2021-11-26 18:19:28 +0100 |
commit | 92bd7850d0e6232d793135ad3f1c8cd4282b4b85 (patch) | |
tree | c8dff9bb9684b40c20bcd9561492b932bf912496 | |
parent | 06c58b99f5e70e5fc7cb3167ab2fddb0703a6324 (diff) | |
parent | 0484486f3f704aecadd35b574f1ab179ce03bcd8 (diff) |
Merge remote-tracking branch 'origin/master' into no-strip/760
-rw-r--r-- | .github/ISSUE_TEMPLATE/bug_report.md | 28 | ||||
-rw-r--r-- | .github/ISSUE_TEMPLATE/bug_report.yaml | 41 | ||||
-rw-r--r-- | CHANGELOG.md | 9 | ||||
-rw-r--r-- | Cargo.lock | 91 | ||||
-rw-r--r-- | Cargo.toml | 7 | ||||
-rw-r--r-- | README.md | 9 | ||||
-rw-r--r-- | contrib/completion/_fd | 1 | ||||
-rw-r--r-- | doc/fd.1 | 50 | ||||
-rw-r--r-- | src/app.rs | 115 | ||||
-rw-r--r-- | src/config.rs | 4 | ||||
-rw-r--r-- | src/exec/job.rs | 15 | ||||
-rw-r--r-- | src/exec/mod.rs | 7 | ||||
-rw-r--r-- | src/exit_codes.rs | 20 | ||||
-rw-r--r-- | src/filter/size.rs | 7 | ||||
-rw-r--r-- | src/filter/time.rs | 4 | ||||
-rw-r--r-- | src/main.rs | 11 | ||||
-rw-r--r-- | src/output.rs | 61 | ||||
-rw-r--r-- | src/walk.rs | 15 | ||||
-rw-r--r-- | tests/tests.rs | 67 |
19 files changed, 444 insertions, 118 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 58eb9f2..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: Bug Report -about: Report a bug. -title: "" -labels: bug -assignees: '' - ---- - -**Describe the bug you encountered:** - -<!-- -Please check out the troubleshooting section first: - https://github.com/sharkdp/fd#troubleshooting ---> - - -**Describe what you expected to happen:** - - -**What version of `fd` are you using?** -<!-- paste the output of `fd --version` here --> - -**Which operating system / distribution are you on?** -<!-- -Unix: paste the output of `uname -srm` and `lsb_release -a` here. -Windows: please tell us your Windows version ---> diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 0000000..bd8c609 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,41 @@ +name: Bug Report +description: Report a bug. +title: "[BUG] " +labels: bug +body: + - type: markdown + attributes: + value: | + Please check out the [troubleshooting section](https://github.com/sharkdp/fd#troubleshooting) first. + - type: checkboxes + attributes: + options: + - label: I have read the troubleshooting section and still think this is a bug. + required: true + - type: textarea + id: bug + attributes: + label: "Describe the bug you encountered:" + validations: + required: true + - type: textarea + id: expected + attributes: + label: "Describe what you expected to happen:" + - type: input + id: version + attributes: + label: "What version of `fd` are you using?" + placeholder: "paste the output of `fd --version` here" + validations: + required: true + - type: textarea + id: os + attributes: + label: Which operating system / distribution are you on? + placeholder: | + Unix: paste the output of `uname -srm` and `lsb_release -a` here. + Windows: please tell us your Windows version + render: shell + validations: + required: true diff --git a/CHANGELOG.md b/CHANGELOG.md index afe8f31..529cd52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,17 @@ - File metadata is now cached between the different filters that require it (e.g. `--owner`, `--size`), reducing the number of `stat` syscalls when multiple filters are used; see #863 +- Colorized output is now significantly faster, see #720 and #853 (@tavianator) ## Features -- Don't buffer command output from `--exec` when using a single thread. See #522 +- Don't buffer command output from `--exec` when using a single thread. See #522 - Add new `-q, --quiet` flag, see #303 (@Asha20) - - Add new `--no-ignore-parent` flag, see #787 (@will459) +- Add new `--batch-size` flag, see #410 (@devonhollowood) +- Add opposing command-line options, see #595 (@Asha20) +- Add support for more filesystem indicators in `LS_COLORS`, see + https://github.com/sharkdp/lscolors/pull/35 (@tavianator) ## Bugfixes @@ -23,6 +27,7 @@ - Support `--list-details` on more platforms (like BusyBox), see #783 - The filters `--owner`, `--size`, and `--changed-{within,before}` now apply to symbolic links themselves, rather than the link target, except when `--follow` is specified; see #863 +- Change time comparisons to be exclusive, see #794 (@jacobmischka) ## Changes @@ -31,9 +31,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" +checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7" [[package]] name = "atty" @@ -54,24 +54,24 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bstr" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" dependencies = [ "memchr", ] [[package]] name = "cc" -version = "1.0.70" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" [[package]] name = "cfg-if" @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "377c9b002a72a0b2c1a18c62e2f3864bdfea4a015e3683a96e24aa45dd6c02d1" +checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf" dependencies = [ "nix", "winapi", @@ -172,15 +172,16 @@ dependencies = [ "humantime", "ignore", "jemallocator", - "lazy_static", "libc", "lscolors", + "nix", "normpath", "num_cpus", "once_cell", "regex", "regex-syntax", "tempdir", + "test-case", "users", "version_check", ] @@ -301,9 +302,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.103" +version = "0.2.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" +checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" [[package]] name = "log" @@ -316,9 +317,9 @@ dependencies = [ [[package]] name = "lscolors" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24b894c45c9da468621cdd615a5a79ee5e5523dd4f75c76ebc03d458940c16e" +checksum = "9dd58d8727f3035fa6d5272f16b519741fd4875936b99d8a7cde21291b7d9174" dependencies = [ "ansi_term 0.12.1", ] @@ -340,9 +341,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.22.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7555d6c7164cc913be1ce7f95cbecdabda61eb2ccd89008524af306fb7f5031" +checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188" dependencies = [ "bitflags", "cc", @@ -353,9 +354,9 @@ dependencies = [ [[package]] name = "normpath" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27e6e8f70e9fbbe3752d330d769e3424f24b9458ce266df93a3b456902fd696a" +checksum = "640c20e9df4a2d4a5adad5b47e17d76dac3e824346b181931c3ec9f7a85687b1" dependencies = [ "winapi", ] @@ -396,6 +397,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" [[package]] +name = "proc-macro2" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] name = "rand" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -493,6 +512,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] +name = "syn" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] name = "tempdir" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -513,6 +543,19 @@ dependencies = [ ] [[package]] +name = "test-case" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7cad0a06f9a61e94355aa3b3dc92d85ab9c83406722b1ca5e918d4297c12c23" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -543,9 +586,15 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.8" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "users" @@ -37,13 +37,12 @@ version_check = "0.9" ansi_term = "0.12" atty = "0.2" ignore = "0.4.3" -lazy_static = "1.1.0" num_cpus = "1.8" regex = "1.5.4" regex-syntax = "0.6" ctrlc = "3.2" humantime = "2.1" -lscolors = "0.7" +lscolors = "0.8" globset = "0.4" anyhow = "1.0" dirs-next = "2.0" @@ -57,6 +56,7 @@ features = ["suggestions", "color", "wrap_help"] [target.'cfg(unix)'.dependencies] users = "0.11.0" +nix = "0.23.0" [target.'cfg(all(unix, not(target_os = "redox")))'.dependencies] libc = "0.2" @@ -70,7 +70,8 @@ jemallocator = "0.3.0" [dev-dependencies] diff = "0.1" tempdir = "0.3" -filetime = "0.2.15" +filetime = "0.2" +test-case = "1.2" [profile.release] lto = true @@ -418,6 +418,14 @@ use a character class with a single hyphen character: > fd '[-]pattern' ``` +### "Command not found" for `alias`es or shell functions + +Shell `alias`es and shell functions can not be used for command execution via `fd -x` or +`fd -X`. In `zsh`, you can make the alias global via `alias -g myalias="…"`. In `bash`, +you can use `export -f my_function` to make available to child processes. You would still +need to call `fd -x bash -c 'my_function "$1"' bash`. For other use cases or shells, use +a (temporary) shell script. + ## Integration with other programs ### Using fd with `fzf` @@ -667,7 +675,6 @@ cargo install --path . - [sharkdp](https://github.com/sharkdp) - [tmccombs](https://github.com/tmccombs) - [tavianator](https://github.com/tavianator) -- [pemistahl](https://github.com/pemistahl/) ## License diff --git a/contrib/completion/_fd b/contrib/completion/_fd index 28b37ee..f8521a7 100644 --- a/contrib/completion/_fd +++ b/contrib/completion/_fd @@ -138,6 +138,7 @@ _fd() { + '(exec-cmds)' # execute command '(long-listing max-results)'{-x+,--exec=}'[execute command for each search result]:command: _command_names -e:*\;::program arguments: _normal' '(long-listing max-results)'{-X+,--exec-batch=}'[execute command for all search results at once]:command: _command_names -e:*\;::program arguments: _normal' + '(long-listing max-results)--batch-size=[max number of args for each -X call]:size' + other '!(--max-buffer-time)--max-buffer-time=[set amount of time to buffer before showing output]:time (ms)' @@ -28,7 +28,7 @@ is a simple, fast and user-friendly alternative to .TP .B \-H, \-\-hidden Include hidden files and directories in the search results -(default: hidden files and directories are skipped). +(default: hidden files and directories are skipped). The flag can be overridden with '--no-hidden'. .TP .B \-I, \-\-no\-ignore Show search results from files and directories that would otherwise be ignored by @@ -49,6 +49,8 @@ The global fd ignore file (usually .I $HOME/.config/fd/ignore ) .RE +.IP +The flag can be overridden with '--ignore'. .TP .B \-u, \-\-unrestricted Alias for '--no-ignore'. Can be repeated; '-uu' is an alias for '--no-ignore --hidden'. @@ -62,6 +64,7 @@ and the global gitignore configuration .RI ( core.excludesFile git setting, which defaults to .IR $HOME/.config/git/ignore ). +The flag can be overridden with '--ignore-vcs'. .TP .B \-s, \-\-case\-sensitive Perform a case-sensitive search. By default, fd uses case-insensitive searches, unless the @@ -84,6 +87,7 @@ performs substring comparison. If you want to match on an exact filename, consid .TP .B \-a, \-\-absolute\-path Shows the full path starting from the root as opposed to relative paths. +The flag can be overridden with '--relative-path'. .TP .B \-l, \-\-list\-details Use a detailed listing format like 'ls -l'. This is basically an alias @@ -93,7 +97,7 @@ sort order. .TP .B \-L, \-\-follow By default, fd does not descend into symlinked directories. Using this flag, symbolic links are -also traversed. +also traversed. The flag can be overridden with '--no-follow'. .TP .B \-p, \-\-full\-path By default, the search pattern is only matched against the filename (or directory name). Using @@ -154,18 +158,40 @@ regular files directories .IP "l, symlink" symbolic links -.IP "x, executable" -executable (files) -.IP "e, empty" -empty files or directories .IP "s, socket" sockets .IP "p, pipe" named pipes (FIFOs) +.IP "x, executable" +executable (files) +.IP "e, empty" +empty files or directories .RE .RS -This option can be used repeatedly to allow for multiple file types. +This option can be specified more than once to include multiple file types. +Searching for '--type file --type symlink' will show both regular files as well as +symlinks. Note that the 'executable' and 'empty' filters work differently: '--type +executable' implies '--type file' by default. And '--type empty' searches for +empty files and directories, unless either '--type file' or '--type directory' is +specified in addition. + +Examples: + - Only search for files: + fd --type file … + fd -tf … + - Find both files and symlinks + fd --type file --type symlink … + fd -tf -tl … + - Find executable files: + fd --type executable + fd -tx + - Find empty files: + fd --type empty --type file + fd -te -tf + - Find empty directories: + fd --type empty --type directory + fd -te -td .RE .TP .BI "\-e, \-\-extension " ext @@ -243,7 +269,7 @@ tebibytes .TP .BI "\-\-changed-within " date|duration Filter results based on the file modification time. -Files with modification times greater than or equal to the argument will be returned. +Files with modification times greater than the argument will be returned. The argument can be provided as a duration (\fI10h, 1d, 35min\fR) or as a specific point in time in either full RFC3339 format with time zone, or as a date or datetime in the local time zone (\fIYYYY-MM-DD\fR or \fIYYYY-MM-DD HH:MM:SS\fR). @@ -256,7 +282,7 @@ Examples: .TP .BI "\-\-changed-before " date|duration Filter results based on the file modification time. -Files with modification times less than or equal to the argument will be returned. +Files with modification times less than the argument will be returned. The argument can be provided as a duration (\fI10h, 1d, 35min\fR) or as a specific point in time in either full RFC3339 format with time zone, or as a date or datetime in the local time zone (\fIYYYY-MM-DD\fR or \fIYYYY-MM-DD HH:MM:SS\fR). @@ -405,5 +431,11 @@ $ fd -e py .TP .RI "Open all search results with vim:" $ fd pattern -X vim +.TP +.BI "\-\-batch\-size " size +Pass at most +.I size +arguments to each call to the command given with -X. +.TP .SH SEE ALSO .BR find (1) @@ -25,7 +25,17 @@ pub fn build_app() -> App<'static, 'static> { .long_help( "Include hidden directories and files in the search results (default: \ hidden files and directories are skipped). Files and directories are \ - considered to be hidden if their name starts with a `.` sign (dot).", + considered to be hidden if their name starts with a `.` sign (dot). \ + The flag can be overridden with --no-hidden.", + ), + ) + .arg( + Arg::with_name("no-hidden") + .long("no-hidden") + .overrides_with("hidden") + .hidden(true) + .long_help( + "Overrides --hidden.", ), ) .arg( @@ -36,7 +46,17 @@ pub fn build_app() -> App<'static, 'static> { .help("Do not respect .(git|fd)ignore files") .long_help( "Show search results from files and directories that would otherwise be \ - ignored by '.gitignore', '.ignore', '.fdignore', or the global ignore file.", + ignored by '.gitignore', '.ignore', '.fdignore', or the global ignore file. \ + The flag can be overridden with --ignore.", + ), + ) + .arg( + Arg::with_name("ignore") + .long("ignore") + .overrides_with("no-ignore") + .hidden(true) + .long_help( + "Overrides --no-ignore.", ), ) .arg( @@ -47,7 +67,16 @@ pub fn build_app() -> App<'static, 'static> { .help("Do not respect .gitignore files") .long_help( "Show search results from files and directories that would otherwise be \ - ignored by '.gitignore' files.", + ignored by '.gitignore' files. The flag can be overridden with --ignore-vcs.", + ), + ) + .arg( + Arg::with_name("ignore-vcs") + .long("ignore-vcs") + .overrides_with("no-ignore-vcs") + .hidden(true) + .long_help( + "Overrides --no-ignore-vcs.", ), ) .arg( @@ -72,6 +101,7 @@ pub fn build_app() -> App<'static, 'static> { Arg::with_name("rg-alias-hidden-ignore") .short("u") .long("unrestricted") + .overrides_with_all(&["ignore", "no-hidden"]) .multiple(true) .hidden_short_help(true) .help("Alias for '--no-ignore', and '--hidden' when given twice") @@ -145,7 +175,17 @@ pub fn build_app() -> App<'static, 'static> { .overrides_with("absolute-path") .help("Show absolute instead of relative paths") .long_help( - "Shows the full path starting from the root as opposed to relative paths.", + "Shows the full path starting from the root as opposed to relative paths. \ + The flag can be overridden with --relative-path.", + ), + ) + .arg( + Arg::with_name("relative-path") + .long("relative-path") + .overrides_with("absolute-path") + .hidden(true) + .long_help( + "Overrides --absolute-path.", ), ) .arg( @@ -170,7 +210,17 @@ pub fn build_app() -> App<'static, 'static> { .help("Follow symbolic links") .long_help( "By default, fd does not descend into symlinked directories. Using this \ - flag, symbolic links are also traversed.", + flag, symbolic links are also traversed. \ + Flag can be overriden with --no-follow.", + ), + ) + .arg( + Arg::with_name("no-follow") + .long("no-follow") + .overrides_with("follow") + .hidden(true) + .long_help( + "Overrides --follow.", ), ) .arg( @@ -178,11 +228,12 @@ pub fn build_app() -> App<'static, 'static> { .long("full-path") .short("p") .overrides_with("full-path") - .help("Search full path (default: file-/dirname only)") + .help("Search full abs. path (default: filename only)") .long_help( "By default, the search pattern is only matched against the filename (or \ - directory name). Using this flag, the pattern is matched against the \ - full path.", + directory name). Using this flag, the pattern is matched against the full \ + (absolute) path. Example:\n \ + fd --glob -p '**/.git/config'", ), ) .arg( @@ -249,7 +300,8 @@ pub fn build_app() -> App<'static, 'static> { .conflicts_with_all(&["size", "exact-depth"]) .hidden_short_help(true) .help("Do not traverse into matching directories") - .long_help("Do not traverse into matching directories.") + .long_help("Do not traverse into directories that match the search criteria. If \ + you want to exclude specific directories, use the '--exclude=…' option.") ) .arg( Arg::with_name("file-type") @@ -281,14 +333,36 @@ pub fn build_app() -> App<'static, 'static> { empty (e), socket (s), pipe (p)", ) .long_help( - "Filter the search by type (multiple allowable filetypes can be specified):\n \ + "Filter the search by type:\n \ 'f' or 'file': regular files\n \ 'd' or 'directory': directories\n \ 'l' or 'symlink': symbolic links\n \ - 'x' or 'executable': executables\n \ - 'e' or 'empty': empty files or directories\n \ 's' or 'socket': socket\n \ - 'p' or 'pipe': named pipe (FIFO)", + 'p' or 'pipe': named pipe (FIFO)\n\n \ + 'x' or 'executable': executables\n \ + 'e' or 'empty': empty files or directories\n\n\ + This option can be specified more than once to include multiple file types. \ + Searching for '--type file --type symlink' will show both regular files as \ + well as symlinks. Note that the 'executable' and 'empty' filters work differently: \ + '--type executable' implies '--type file' by default. And '--type empty' searches \ + for empty files and directories, unless either '--type file' or '--type directory' \ + is specified in addition.\n\n\ + Examples:\n \ + - Only search for files:\n \ + fd --type file …\n \ + fd -tf …\n \ + - Find both files and symlinks\n \ + fd --type file --type symlink …\n \ + fd -tf -tl …\n \ + - Find executable files:\n \ + fd --type executable\n \ + fd -tx\n \ + - Find empty files:\n \ + fd --type empty --type file\n \ + fd -te -tf\n \ + - Find empty directories:\n \ + fd --type empty --type directory\n \ + fd -te -td" ), ) .arg( @@ -366,6 +440,21 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( + Arg::with_name("batch-size") + .long("batch-size") + .takes_value(true) + .value_name("size") + .hidden_short_help(true) + .requires("exec-batch") + .help("Max number of arguments to run as a batch with -X") + .long_help( + "Maximum number of arguments to pass to the command given with -X. \ + If the number of results is greater than the given size, \ + the command given with -X is run again with remaining arguments. \ + A batch size of zero means there is no limit.", + ), + ) + .arg( Arg::with_name("exclude") .long("exclude") .short("E") diff --git a/src/config.rs b/src/config.rs index 91ca601..5a71714 100644 --- a/src/config.rs +++ b/src/config.rs @@ -85,6 +85,10 @@ pub struct Config { /// If a value is supplied, each item found will be used to generate and execute commands. pub command: Option<Arc<CommandTemplate>>, + /// Maximum number of search results to pass to each `command`. If zero, the number is + /// unlimited. + pub batch_size: usize, + /// A list of glob patterns that should be excluded from the search. pub exclude_patterns: Vec<String>, diff --git a/src/exec/job.rs b/src/exec/job.rs index 83abf1a..aa8164c 100644 --- a/src/exec/job.rs +++ b/src/exec/job.rs @@ -50,6 +50,7 @@ pub fn batch( cmd: &CommandTemplate, show_filesystem_errors: bool, buffer_output: bool, + limit: usize, ) -> ExitCode { let paths = rx.iter().filter_map(|value| match value { WorkerResult::Entry(val) => Some(val), @@ -60,5 +61,17 @@ pub fn batch( None } }); - cmd.generate_and_execute_batch(paths, buffer_output) + if limit == 0 { + // no limit + return cmd.generate_and_execute_batch(paths, buffer_output); + } + + let mut exit_codes = Vec::new(); + let mut peekable = paths.peekable(); + while peekable.peek().is_some() { + let limited = peekable.by_ref().take(limit); + let exit_code = cmd.generate_and_execute_batch(limited, buffer_output); + exit_codes.push(exit_code); + } + merge_exitcodes(exit_codes) } diff --git a/src/exec/mod.rs b/src/exec/mod.rs index 15e1ae5..3f93acd 100644 --- a/src/exec/mod.rs +++ b/src/exec/mod.rs @@ -10,7 +10,7 @@ use std::process::{Command, Stdio}; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, Result}; -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use regex::Regex; use crate::exit_codes::ExitCode; @@ -71,9 +71,8 @@ impl CommandTemplate { I: IntoIterator<Item = S>, S: AsRef<str>, { - lazy_static! { - static ref PLACEHOLDER_PATTERN: Regex = Regex::new(r"\{(/?\.?|//)\}").unwrap(); - } + static PLACEHOLDER_PATTERN: Lazy<Regex> = + Lazy::new(|| Regex::new(r"\{(/?\.?|//)\}").unwrap()); let mut args = Vec::new(); let mut has_placeholder = false; diff --git a/src/exit_codes.rs b/src/exit_codes.rs index 4f8a974..e44111b 100644 --- a/src/exit_codes.rs +++ b/src/exit_codes.rs @@ -1,3 +1,8 @@ +use std::process; + +#[cfg(unix)] +use nix::sys::signal::{raise, signal, SigHandler, Signal}; + #[derive(Debug, Clone, Copy, PartialEq)] pub enum ExitCode { Success, @@ -21,6 +26,21 @@ impl ExitCode { fn is_error(self) -> bool { i32::from(self) != 0 } + + /// Exit the process with the appropriate code. + pub fn exit(self) -> ! { + #[cfg(unix)] + if self == ExitCode::KilledBySigint { + // Get rid of the SIGINT handler, if present, and raise SIGINT + unsafe { + if signal(Signal::SIGINT, SigHandler::SigDfl).is_ok() { + let _ = raise(Signal::SIGINT); + } + } + } + + process::exit(self.into()) + } } pub fn merge_exitcodes(results: impl IntoIterator<Item = ExitCode>) -> ExitCode { diff --git a/src/filter/size.rs b/src/filter/size.rs index 5435b19..2af20bc 100644 --- a/src/filter/size.rs +++ b/src/filter/size.rs @@ -1,9 +1,8 @@ -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use regex::Regex; -lazy_static! { - static ref SIZE_CAPTURES: Regex = Regex::new(r"(?i)^([+-]?)(\d+)(b|[kmgt]i?b?)$").unwrap(); -} +static SIZE_CAPTURES: Lazy<Regex> = + Lazy::new(|| Regex::new(r"(?i)^([+-]?)(\d+)(b|[kmgt]i?b?)$").unwrap()); #[derive(Clone, Copy, Debug, PartialEq)] pub enum SizeFilter { diff --git a/src/filter/time.rs b/src/filter/time.rs index 39979af..eb43c4b 100644 --- a/src/filter/time.rs +++ b/src/filter/time.rs @@ -39,8 +39,8 @@ impl TimeFilter { pub fn applies_to(&self, t: &SystemTime) -> bool { match self { - TimeFilter::Before(limit) => t <= limit, - TimeFilter::After(limit) => t >= limit, + TimeFilter::Before(limit) => t < limit, + TimeFilter::After(limit) => t > limit, } } } diff --git a/src/main.rs b/src/main.rs index 974c561..cb6dfa8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12, |