Implement --batch-size (#866)

This commit is contained in:
Devon Hollowood 2021-10-21 23:05:13 -07:00 committed by GitHub
parent 7b5b3ec47b
commit 17dd2a6dfe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 97 additions and 2 deletions

View file

@ -12,6 +12,8 @@
- Add new `--no-ignore-parent` flag, see #787 (@will459)
- Add new `--batch-size` flag, see #410 (@devonhollowood)
## Bugfixes
- Set default path separator to `/` in MSYS, see #537 and #730 (@aswild)

View file

@ -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)'

6
doc/fd.1 vendored
View file

@ -405,5 +405,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)

View file

@ -365,6 +365,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")

View file

@ -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>,

View file

@ -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)
}

View file

@ -348,6 +348,12 @@ fn construct_config(matches: clap::ArgMatches, pattern_regex: &str) -> Result<Co
})
.transpose()?,
command: command.map(Arc::new),
batch_size: matches
.value_of("batch-size")
.map(|n| n.parse::<usize>())
.transpose()
.context("Failed to parse --batch-size argument")?
.unwrap_or_default(),
exclude_patterns: matches
.values_of("exclude")
.map(|v| v.map(|p| String::from("!") + p).collect())

View file

@ -179,7 +179,13 @@ fn spawn_receiver(
// This will be set to `Some` if the `--exec` argument was supplied.
if let Some(ref cmd) = config.command {
if cmd.in_batch_mode() {
exec::batch(rx, cmd, show_filesystem_errors, enable_output_buffering)
exec::batch(
rx,
cmd,
show_filesystem_errors,
enable_output_buffering,
config.batch_size,
)
} else {
let shared_rx = Arc::new(Mutex::new(rx));

View file

@ -1418,6 +1418,48 @@ fn test_exec_batch() {
}
}
#[test]
fn test_exec_batch_with_limit() {
// TODO Test for windows
if cfg!(windows) {
return;
}
let te = TestEnv::new(DEFAULT_DIRS, DEFAULT_FILES);
te.assert_output(
&["foo", "--batch-size", "0", "--exec-batch", "echo", "{}"],
"a.foo one/b.foo one/two/C.Foo2 one/two/c.foo one/two/three/d.foo one/two/three/directory_foo",
);
let output = te.assert_success_and_get_output(
".",
&["foo", "--batch-size=2", "--exec-batch", "echo", "{}"],
);
let stdout = String::from_utf8_lossy(&output.stdout);
for line in stdout.lines() {
assert_eq!(2, line.split_whitespace().count());
}
let mut paths: Vec<_> = stdout
.lines()
.flat_map(|line| line.split_whitespace())
.collect();
paths.sort_unstable();
assert_eq!(
&paths,
&[
"a.foo",
"one/b.foo",
"one/two/C.Foo2",
"one/two/c.foo",
"one/two/three/d.foo",
"one/two/three/directory_foo"
],
);
}
/// Shell script execution (--exec) with a custom --path-separator
#[test]
fn test_exec_with_separator() {