mirror of
https://github.com/sharkdp/fd.git
synced 2024-11-19 02:10:34 +01:00
Implement --batch-size
(#866)
This commit is contained in:
parent
7b5b3ec47b
commit
17dd2a6dfe
9 changed files with 97 additions and 2 deletions
|
@ -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)
|
||||
|
|
|
@ -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
6
doc/fd.1
vendored
|
@ -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)
|
||||
|
|
15
src/app.rs
15
src/app.rs
|
@ -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")
|
||||
|
|
|
@ -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>,
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue