From 0f0fa76c81dc95bbd7a9097b62698352af640940 Mon Sep 17 00:00:00 2001 From: Max Triano Date: Thu, 18 Jun 2020 11:10:23 -0400 Subject: [PATCH 01/12] Just trying some things out, initial functionality --- src/exec/command.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/exec/command.rs b/src/exec/command.rs index ecdfe62..99e4640 100644 --- a/src/exec/command.rs +++ b/src/exec/command.rs @@ -9,10 +9,24 @@ use crate::exit_codes::ExitCode; /// Executes a command. pub fn execute_command(mut cmd: Command, out_perm: &Mutex<()>) -> ExitCode { // Spawn the supplied command. - let output = cmd.output(); + /* let output = cmd.output(); */ + // This sort of works: + let child = cmd.spawn().expect("failed to wait on child"); + let output = child + .wait_with_output() + .expect("failed to wait on child"); + ExitCode::Success + /* let child_handle = cmd.spawn(); + match child_handle { + Ok(output) => { + let exit_code = output.wait(); + } + Err() => { + } + } */ // Then wait for the command to exit, if it was spawned. - match output { + /* match output { Ok(output) => { // While this lock is active, this thread will be the only thread allowed // to write its outputs. @@ -21,8 +35,8 @@ pub fn execute_command(mut cmd: Command, out_perm: &Mutex<()>) -> ExitCode { let stdout = io::stdout(); let stderr = io::stderr(); - let _ = stdout.lock().write_all(&output.stdout); - let _ = stderr.lock().write_all(&output.stderr); + //let _ = stdout.lock().write_all(&output.stdout); + //let _ = stderr.lock().write_all(&output.stderr); if output.status.code() == Some(0) { ExitCode::Success @@ -38,5 +52,5 @@ pub fn execute_command(mut cmd: Command, out_perm: &Mutex<()>) -> ExitCode { print_error(format!("Problem while executing command: {}", why)); ExitCode::GeneralError } - } + } */ } From 394e967b5fa2d6b19068ff34bef0ecbd1591c490 Mon Sep 17 00:00:00 2001 From: Max Triano Date: Tue, 23 Jun 2020 11:29:04 -0400 Subject: [PATCH 02/12] No buffering cmd output when running on 1 thread --- src/exec/command.rs | 35 ++++++++++++++--------------------- src/exec/job.rs | 8 +++++--- src/exec/mod.rs | 8 ++++---- src/walk.rs | 10 +++++++--- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/exec/command.rs b/src/exec/command.rs index 99e4640..1baa15d 100644 --- a/src/exec/command.rs +++ b/src/exec/command.rs @@ -7,26 +7,19 @@ use crate::error::print_error; use crate::exit_codes::ExitCode; /// Executes a command. -pub fn execute_command(mut cmd: Command, out_perm: &Mutex<()>) -> ExitCode { +pub fn execute_command(mut cmd: Command, out_perm: &Mutex<()>, is_multithread: bool) -> ExitCode { + let output; // Spawn the supplied command. - /* let output = cmd.output(); */ - // This sort of works: - let child = cmd.spawn().expect("failed to wait on child"); - let output = child - .wait_with_output() - .expect("failed to wait on child"); - ExitCode::Success - /* let child_handle = cmd.spawn(); - match child_handle { - Ok(output) => { - let exit_code = output.wait(); - } - Err() => { - - } - } */ + if is_multithread { + output = cmd.output(); + } else { + // This sort of works: + let child = cmd.spawn().expect("Failed to execute command"); + output = child.wait_with_output(); + } + // Then wait for the command to exit, if it was spawned. - /* match output { + match output { Ok(output) => { // While this lock is active, this thread will be the only thread allowed // to write its outputs. @@ -35,8 +28,8 @@ pub fn execute_command(mut cmd: Command, out_perm: &Mutex<()>) -> ExitCode { let stdout = io::stdout(); let stderr = io::stderr(); - //let _ = stdout.lock().write_all(&output.stdout); - //let _ = stderr.lock().write_all(&output.stderr); + let _ = stdout.lock().write_all(&output.stdout); + let _ = stderr.lock().write_all(&output.stderr); if output.status.code() == Some(0) { ExitCode::Success @@ -52,5 +45,5 @@ pub fn execute_command(mut cmd: Command, out_perm: &Mutex<()>) -> ExitCode { print_error(format!("Problem while executing command: {}", why)); ExitCode::GeneralError } - } */ + } } diff --git a/src/exec/job.rs b/src/exec/job.rs index eef042a..9b27516 100644 --- a/src/exec/job.rs +++ b/src/exec/job.rs @@ -16,6 +16,7 @@ pub fn job( cmd: Arc, out_perm: Arc>, show_filesystem_errors: bool, + is_multithread: bool, ) -> ExitCode { let mut results: Vec = Vec::new(); loop { @@ -34,11 +35,11 @@ pub fn job( } Err(_) => break, }; - + // Drop the lock so that other threads can read from the receiver. drop(lock); // Generate a command, execute it and store its exit code. - results.push(cmd.generate_and_execute(&value, Arc::clone(&out_perm))) + results.push(cmd.generate_and_execute(&value, Arc::clone(&out_perm), is_multithread)) } // Returns error in case of any error. merge_exitcodes(&results) @@ -48,6 +49,7 @@ pub fn batch( rx: Receiver, cmd: &CommandTemplate, show_filesystem_errors: bool, + is_multithread: bool, ) -> ExitCode { let paths = rx.iter().filter_map(|value| match value { WorkerResult::Entry(val) => Some(val), @@ -58,5 +60,5 @@ pub fn batch( None } }); - cmd.generate_and_execute_batch(paths) + cmd.generate_and_execute_batch(paths, is_multithread) } diff --git a/src/exec/mod.rs b/src/exec/mod.rs index f24c0e3..9a3d6df 100644 --- a/src/exec/mod.rs +++ b/src/exec/mod.rs @@ -139,7 +139,7 @@ impl CommandTemplate { /// /// Using the internal `args` field, and a supplied `input` variable, a `Command` will be /// build. Once all arguments have been processed, the command is executed. - pub fn generate_and_execute(&self, input: &Path, out_perm: Arc>) -> ExitCode { + pub fn generate_and_execute(&self, input: &Path, out_perm: Arc>, is_multithread: bool) -> ExitCode { let input = strip_current_dir(input); let mut cmd = Command::new(self.args[0].generate(&input, self.path_separator.as_deref())); @@ -147,14 +147,14 @@ impl CommandTemplate { cmd.arg(arg.generate(&input, self.path_separator.as_deref())); } - execute_command(cmd, &out_perm) + execute_command(cmd, &out_perm, is_multithread) } pub fn in_batch_mode(&self) -> bool { self.mode == ExecutionMode::Batch } - pub fn generate_and_execute_batch(&self, paths: I) -> ExitCode + pub fn generate_and_execute_batch(&self, paths: I, is_multithread: bool) -> ExitCode where I: Iterator, { @@ -182,7 +182,7 @@ impl CommandTemplate { } if has_path { - execute_command(cmd, &Mutex::new(())) + execute_command(cmd, &Mutex::new(()), is_multithread) } else { ExitCode::Success } diff --git a/src/walk.rs b/src/walk.rs index e2966b3..9da49aa 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -170,12 +170,16 @@ fn spawn_receiver( let show_filesystem_errors = config.show_filesystem_errors; let threads = config.threads; - + let is_multithread: bool = if threads > 1 { + true + } else { + false + }; thread::spawn(move || { // 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) + exec::batch(rx, cmd, show_filesystem_errors, is_multithread) } else { let shared_rx = Arc::new(Mutex::new(rx)); @@ -190,7 +194,7 @@ fn spawn_receiver( // Spawn a job thread that will listen for and execute inputs. let handle = - thread::spawn(move || exec::job(rx, cmd, out_perm, show_filesystem_errors)); + thread::spawn(move || exec::job(rx, cmd, out_perm, show_filesystem_errors, is_multithread)); // Push the handle of the spawned thread into the vector for later joining. handles.push(handle); From 154cd6e32971e63ecfcffd8a62d4fecd88e75651 Mon Sep 17 00:00:00 2001 From: Max Triano Date: Tue, 23 Jun 2020 15:28:15 -0400 Subject: [PATCH 03/12] Clarifying comments --- src/exec/command.rs | 3 ++- src/walk.rs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/exec/command.rs b/src/exec/command.rs index 1baa15d..5d546f2 100644 --- a/src/exec/command.rs +++ b/src/exec/command.rs @@ -13,7 +13,8 @@ pub fn execute_command(mut cmd: Command, out_perm: &Mutex<()>, is_multithread: b if is_multithread { output = cmd.output(); } else { - // This sort of works: + // If running on only one thread, don't buffer output + // Allows for viewing and interacting with intermediate command output let child = cmd.spawn().expect("Failed to execute command"); output = child.wait_with_output(); } diff --git a/src/walk.rs b/src/walk.rs index 9da49aa..866205e 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -170,6 +170,7 @@ fn spawn_receiver( let show_filesystem_errors = config.show_filesystem_errors; let threads = config.threads; + // This will be used to check if output should be buffered when only running a single thread let is_multithread: bool = if threads > 1 { true } else { From e4730603d05802e402f7139f0f889f034352155f Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Mon, 9 Aug 2021 01:02:30 -0600 Subject: [PATCH 04/12] Address feedback for removing buffering when running on a single thread --- src/exec/command.rs | 18 ++++++++++-------- src/exec/job.rs | 10 +++++----- src/exec/mod.rs | 13 +++++++++---- src/walk.rs | 19 +++++++++++-------- 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/exec/command.rs b/src/exec/command.rs index 5d546f2..9c2c5e2 100644 --- a/src/exec/command.rs +++ b/src/exec/command.rs @@ -7,18 +7,20 @@ use crate::error::print_error; use crate::exit_codes::ExitCode; /// Executes a command. -pub fn execute_command(mut cmd: Command, out_perm: &Mutex<()>, is_multithread: bool) -> ExitCode { - let output; +pub fn execute_command( + mut cmd: Command, + out_perm: &Mutex<()>, + enable_output_buffering: bool, +) -> ExitCode { // Spawn the supplied command. - if is_multithread { - output = cmd.output(); + let output = if enable_output_buffering { + cmd.output() } else { // If running on only one thread, don't buffer output // Allows for viewing and interacting with intermediate command output - let child = cmd.spawn().expect("Failed to execute command"); - output = child.wait_with_output(); - } - + cmd.spawn().and_then(|c| c.wait_with_output()) + }; + // Then wait for the command to exit, if it was spawned. match output { Ok(output) => { diff --git a/src/exec/job.rs b/src/exec/job.rs index 9b27516..c43be5b 100644 --- a/src/exec/job.rs +++ b/src/exec/job.rs @@ -16,7 +16,7 @@ pub fn job( cmd: Arc, out_perm: Arc>, show_filesystem_errors: bool, - is_multithread: bool, + buffer_output: bool, ) -> ExitCode { let mut results: Vec = Vec::new(); loop { @@ -35,11 +35,11 @@ pub fn job( } Err(_) => break, }; - + // Drop the lock so that other threads can read from the receiver. drop(lock); // Generate a command, execute it and store its exit code. - results.push(cmd.generate_and_execute(&value, Arc::clone(&out_perm), is_multithread)) + results.push(cmd.generate_and_execute(&value, Arc::clone(&out_perm), buffer_output)) } // Returns error in case of any error. merge_exitcodes(&results) @@ -49,7 +49,7 @@ pub fn batch( rx: Receiver, cmd: &CommandTemplate, show_filesystem_errors: bool, - is_multithread: bool, + buffer_output: bool, ) -> ExitCode { let paths = rx.iter().filter_map(|value| match value { WorkerResult::Entry(val) => Some(val), @@ -60,5 +60,5 @@ pub fn batch( None } }); - cmd.generate_and_execute_batch(paths, is_multithread) + cmd.generate_and_execute_batch(paths, buffer_output) } diff --git a/src/exec/mod.rs b/src/exec/mod.rs index 9a3d6df..d1f7d74 100644 --- a/src/exec/mod.rs +++ b/src/exec/mod.rs @@ -139,7 +139,12 @@ impl CommandTemplate { /// /// Using the internal `args` field, and a supplied `input` variable, a `Command` will be /// build. Once all arguments have been processed, the command is executed. - pub fn generate_and_execute(&self, input: &Path, out_perm: Arc>, is_multithread: bool) -> ExitCode { + pub fn generate_and_execute( + &self, + input: &Path, + out_perm: Arc>, + buffer_output: bool, + ) -> ExitCode { let input = strip_current_dir(input); let mut cmd = Command::new(self.args[0].generate(&input, self.path_separator.as_deref())); @@ -147,14 +152,14 @@ impl CommandTemplate { cmd.arg(arg.generate(&input, self.path_separator.as_deref())); } - execute_command(cmd, &out_perm, is_multithread) + execute_command(cmd, &out_perm, buffer_output) } pub fn in_batch_mode(&self) -> bool { self.mode == ExecutionMode::Batch } - pub fn generate_and_execute_batch(&self, paths: I, is_multithread: bool) -> ExitCode + pub fn generate_and_execute_batch(&self, paths: I, buffer_output: bool) -> ExitCode where I: Iterator, { @@ -182,7 +187,7 @@ impl CommandTemplate { } if has_path { - execute_command(cmd, &Mutex::new(()), is_multithread) + execute_command(cmd, &Mutex::new(()), buffer_output) } else { ExitCode::Success } diff --git a/src/walk.rs b/src/walk.rs index 866205e..bcb15d4 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -171,16 +171,12 @@ fn spawn_receiver( let show_filesystem_errors = config.show_filesystem_errors; let threads = config.threads; // This will be used to check if output should be buffered when only running a single thread - let is_multithread: bool = if threads > 1 { - true - } else { - false - }; + let enable_output_buffering: bool = threads > 1; thread::spawn(move || { // 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, is_multithread) + exec::batch(rx, cmd, show_filesystem_errors, enable_output_buffering) } else { let shared_rx = Arc::new(Mutex::new(rx)); @@ -194,8 +190,15 @@ fn spawn_receiver( let out_perm = Arc::clone(&out_perm); // Spawn a job thread that will listen for and execute inputs. - let handle = - thread::spawn(move || exec::job(rx, cmd, out_perm, show_filesystem_errors, is_multithread)); + let handle = thread::spawn(move || { + exec::job( + rx, + cmd, + out_perm, + show_filesystem_errors, + enable_output_buffering, + ) + }); // Push the handle of the spawned thread into the vector for later joining. handles.push(handle); From 049232439ab1bc19b08739eb29852ab967eb7d6c Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Tue, 10 Aug 2021 00:57:04 -0600 Subject: [PATCH 05/12] Add item to CHANGELOG for no buffer with single thread --- CHANGELOG.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35ba6b6..c8280b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Upcoming release ## Features +- Don't buffer command output from `--exec` when using a single thread. See #522 ## Bugfixes @@ -457,7 +458,7 @@ I'd also like to take this chance to say a special Thank You to a few people tha * Add option to force colored output: `--color always`, see #49 (@Detegr) * Generate Shell completions for Bash, ZSH, Fish and Powershell, see #64 (@ImbaKnugel) * Better & extended `--help` text (@abaez and @Detegr) -* Proper Windows support, see #70 +* Proper Windows support, see #70 ## Changes @@ -485,9 +486,9 @@ I'd also like to take this chance to say a special Thank You to a few people tha * Changed `--sensitive` to `--case-sensitive` * Changed `--absolute` to `--absolute-path` -* Throw an error if root directory is not existent, see #39 -* Use absolute paths if the root dir is an absolute path, see #40 -* Handle invalid UTF-8, see #34 #38 +* Throw an error if root directory is not existent, see #39 +* Use absolute paths if the root dir is an absolute path, see #40 +* Handle invalid UTF-8, see #34 #38 * Support `-V`, `--version` by switching from `getopts` to `clap`. Misc: @@ -495,8 +496,8 @@ Misc: # v1.1.0 -- Windows compatibility (@sebasv), see #29 #35 -- Safely exit on broken output pipes (e.g.: usage with `head`, `tail`, ..), see #24 +- Windows compatibility (@sebasv), see #29 #35 +- Safely exit on broken output pipes (e.g.: usage with `head`, `tail`, ..), see #24 - Backport for rust 1.16, see #23 # v1.0.0 @@ -508,17 +509,17 @@ Misc: # v0.3.0 -- Parse dircolors files, closes #20 -- Colorize each path component, closes #19 -- Add short command line option for --hidden, see #18 +- Parse dircolors files, closes #20 +- Colorize each path component, closes #19 +- Add short command line option for --hidden, see #18 # v0.2.0 -- Option to follow symlinks, disable colors, closes #16, closes #17 +- Option to follow symlinks, disable colors, closes #16, closes #17 - `--filename` instead of `--full-path` -- Option to search hidden directories, closes #12 -- Configurable search depth, closes #13 -- Detect interactive terminal, closes #11 +- Option to search hidden directories, closes #12 +- Configurable search depth, closes #13 +- Detect interactive terminal, closes #11 # v0.1.0 From 5b4869a940ecba3afef71894b019507d9d271474 Mon Sep 17 00:00:00 2001 From: a1346054 <36859588+a1346054@users.noreply.github.com> Date: Sat, 14 Aug 2021 09:46:53 +0000 Subject: [PATCH 06/12] fix spelling --- src/exec/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/exec/mod.rs b/src/exec/mod.rs index d1f7d74..a364e86 100644 --- a/src/exec/mod.rs +++ b/src/exec/mod.rs @@ -450,11 +450,11 @@ mod tests { // This is uncommon, but valid check!(r"C:foo\bar", "C:foo#bar"); - // forward slashses should get normalized and interpreted as separators + // forward slashes should get normalized and interpreted as separators check!("C:/foo/bar", "C:#foo#bar"); check!("C:foo/bar", "C:foo#bar"); - // Rust does not intrepret "//server/share" as a UNC path, but rather as a normal + // Rust does not interpret "//server/share" as a UNC path, but rather as a normal // absolute path that begins with RootDir, and the two slashes get combined together as // a single path separator during normalization. //check!("//server/share/path", "##server#share#path"); From 1c72f80ff5f8cd3580085f2ead6819f8cdae0b65 Mon Sep 17 00:00:00 2001 From: a1346054 <36859588+a1346054@users.noreply.github.com> Date: Sat, 14 Aug 2021 09:47:04 +0000 Subject: [PATCH 07/12] fix whitespace --- .github/ISSUE_TEMPLATE/config.yml | 1 - .github/ISSUE_TEMPLATE/feature_request.md | 2 -- doc/screencast.sh | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index b92c70c..0086358 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,2 +1 @@ blank_issues_enabled: true - diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 96f590e..93af6cd 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -6,5 +6,3 @@ labels: feature-request assignees: '' --- - - diff --git a/doc/screencast.sh b/doc/screencast.sh index a4161b4..fe73b94 100644 --- a/doc/screencast.sh +++ b/doc/screencast.sh @@ -20,7 +20,7 @@ enter() { } prompt() { - printf '%b ' $PROMPT | pv -q + printf '%b ' "$PROMPT" | pv -q } type() { From 334488cab7709787ac1a7a0c6bfd4d3fd13b0cf7 Mon Sep 17 00:00:00 2001 From: Asha20 Date: Sat, 14 Aug 2021 17:57:01 +0200 Subject: [PATCH 08/12] Add -q/--quiet/--has-match flag (#813) Instead of printing to stdout, the program will return 1 as the exit code if there are no matches and 0 otherwise. --- CHANGELOG.md | 2 ++ doc/fd.1 | 7 +++++++ src/app.rs | 14 ++++++++++++++ src/exit_codes.rs | 4 +++- src/main.rs | 1 + src/options.rs | 4 ++++ src/walk.rs | 10 +++++++++- tests/tests.rs | 13 +++++++++++++ 8 files changed, 53 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8280b3..197c6ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## Features - Don't buffer command output from `--exec` when using a single thread. See #522 +- Add new `-q, --quiet` flag, see #303 (@Asha20) + ## Bugfixes - Set default path separator to `/` in MSYS, see #537 and #730 (@aswild) diff --git a/doc/fd.1 b/doc/fd.1 index 3dfec2e..487ffd0 100644 --- a/doc/fd.1 +++ b/doc/fd.1 @@ -93,6 +93,13 @@ Limit the number of search results to 'count' and quit immediately. .B \-1 Limit the search to a single result and quit immediately. This is an alias for '--max-results=1'. .TP +.B \-q, \-\-quiet +When the flag is present, the program does not print anything and will instead exit with a code of 0 if there is at least one search result. +Otherwise, the exit code will be 1. +This is mainly for usage in scripts and can be faster than checking for output because the search can be stopped early after the first match. +.B \-\-has\-results +can be used as an alias. +.TP .B \-\-show-errors Enable the display of filesystem errors for situations such as insufficient permissions or dead symlinks. diff --git a/src/app.rs b/src/app.rs index 2d6ec94..7139e9d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -504,6 +504,20 @@ pub fn build_app() -> App<'static, 'static> { .long_help("Limit the search to a single result and quit immediately. \ This is an alias for '--max-results=1'.") ) + .arg( + Arg::with_name("quiet") + .long("quiet") + .short("q") + .alias("has-results") + .hidden_short_help(true) + .conflicts_with_all(&["exec", "exec-batch", "list-details", "max-results"]) + .long_help( + "When the flag is present, the program does not print anything and will \ + return with an exit code of 0 if there is at least one match. Otherwise, the \ + exit code will be 1. \ + '--has-results' can be used as an alias." + ) + ) .arg( Arg::with_name("show-errors") .long("show-errors") diff --git a/src/exit_codes.rs b/src/exit_codes.rs index 720440f..2083b32 100644 --- a/src/exit_codes.rs +++ b/src/exit_codes.rs @@ -1,6 +1,7 @@ #[derive(Debug, Clone, Copy, PartialEq)] pub enum ExitCode { Success, + HasResults(bool), GeneralError, KilledBySigint, } @@ -9,6 +10,7 @@ impl From for i32 { fn from(code: ExitCode) -> Self { match code { ExitCode::Success => 0, + ExitCode::HasResults(has_results) => !has_results as i32, ExitCode::GeneralError => 1, ExitCode::KilledBySigint => 130, } @@ -17,7 +19,7 @@ impl From for i32 { impl ExitCode { fn is_error(self) -> bool { - self != ExitCode::Success + i32::from(self) != 0 } } diff --git a/src/main.rs b/src/main.rs index 7acba88..f0ce9a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -346,6 +346,7 @@ fn run() -> Result { follow_links: matches.is_present("follow"), one_file_system: matches.is_present("one-file-system"), null_separator: matches.is_present("null_separator"), + quiet: matches.is_present("quiet"), max_depth: matches .value_of("max-depth") .or_else(|| matches.value_of("rg-depth")) diff --git a/src/options.rs b/src/options.rs index aa9b5ea..c17dd4d 100644 --- a/src/options.rs +++ b/src/options.rs @@ -54,6 +54,10 @@ pub struct Options { /// The number of threads to use. pub threads: usize, + /// If true, the program doesn't print anything and will instead return an exit code of 0 + /// if there's at least one match. Otherwise, the exit code will be 1. + pub quiet: bool, + /// Time to buffer results internally before streaming to the console. This is useful to /// provide a sorted output, in case the total execution time is shorter than /// `max_buffer_time`. diff --git a/src/walk.rs b/src/walk.rs index bcb15d4..a053278 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -233,6 +233,10 @@ fn spawn_receiver( for worker_result in rx { match worker_result { WorkerResult::Entry(value) => { + if config.quiet { + return ExitCode::HasResults(true); + } + match mode { ReceiverMode::Buffering => { buffer.push(value); @@ -286,7 +290,11 @@ fn spawn_receiver( } } - ExitCode::Success + if config.quiet { + ExitCode::HasResults(false) + } else { + ExitCode::Success + } } }) } diff --git a/tests/tests.rs b/tests/tests.rs index 0b6e6b5..724abe1 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1425,6 +1425,19 @@ fn test_exec_with_separator() { ); } +/// Non-zero exit code (--quiet) +#[test] +fn test_quiet() { + let dirs = &[]; + let files = &["a.foo", "b.foo"]; + let te = TestEnv::new(dirs, files); + + te.assert_output(&["-q"], ""); + te.assert_output(&["--quiet"], ""); + te.assert_output(&["--has-results"], ""); + te.assert_failure_with_error(&["--quiet", "c.foo"], "") +} + /// Literal search (--fixed-strings) #[test] fn test_fixed_strings() { From 1b52948e42e612638131f376fee7415e08c4a38c Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Tue, 10 Aug 2021 01:09:18 -0600 Subject: [PATCH 09/12] Add better documentation of vcs-ignore files. Explicitly list which files are used for gitignore. Fixes #354 --- doc/fd.1 | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/doc/fd.1 b/doc/fd.1 index 487ffd0..265e298 100644 --- a/doc/fd.1 +++ b/doc/fd.1 @@ -32,7 +32,7 @@ Include hidden files and directories in the search results .TP .B \-I, \-\-no\-ignore Show search results from files and directories that would otherwise be ignored by -.IR .gitignore , +any of the files ignored by the '\-\-no\-ignore\-vcs' option, .IR .ignore , .IR .fdignore , or the global ignore file. @@ -41,9 +41,15 @@ or the global ignore file. Alias for '--no-ignore'. Can be repeated; '-uu' is an alias for '--no-ignore --hidden'. .TP .B \-\-no\-ignore\-vcs -Show search results from files and directories that would otherwise be ignored by -.I .gitignore -files. +Show search results from files and directories that would otherwise be ignored by gitignore files +including +.IR .gitignore , +.IR .git/info/exclude , +and the global gitignore configuration ( +.I core.excludesFile +git setting, which defaults to +.I $HOME/.config/git/ignore +). .TP .B \-s, \-\-case\-sensitive Perform a case-sensitive search. By default, fd uses case-insensitive searches, unless the From 78e21395b8d31f20d67149915c001436e89f3b96 Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Wed, 11 Aug 2021 01:22:01 -0600 Subject: [PATCH 10/12] Make list of ignore files for `--no-ignore` more explicit --- doc/fd.1 | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/doc/fd.1 b/doc/fd.1 index 265e298..69e8438 100644 --- a/doc/fd.1 +++ b/doc/fd.1 @@ -32,10 +32,23 @@ Include hidden files and directories in the search results .TP .B \-I, \-\-no\-ignore Show search results from files and directories that would otherwise be ignored by -any of the files ignored by the '\-\-no\-ignore\-vcs' option, -.IR .ignore , -.IR .fdignore , -or the global ignore file. +.RS +.IP \[bu] 2 +.I .gitignore +.IP \[bu] +.I .git/info/exclude +.IP \[bu] +The global gitignore configuration (by default +.IR $HOME/.config/git/ignore ) +.IP \[bu] +.I .ignore +.IP \[bu] +.I .fdignore +.IP \[bu] +The global fd ignore file (usually +.I $HOME/.config/fd/ignore +) +.RE .TP .B \-u, \-\-unrestricted Alias for '--no-ignore'. Can be repeated; '-uu' is an alias for '--no-ignore --hidden'. @@ -45,11 +58,10 @@ Show search results from files and directories that would otherwise be ignored b including .IR .gitignore , .IR .git/info/exclude , -and the global gitignore configuration ( -.I core.excludesFile +and the global gitignore configuration +.RI ( core.excludesFile git setting, which defaults to -.I $HOME/.config/git/ignore -). +.IR $HOME/.config/git/ignore ). .TP .B \-s, \-\-case\-sensitive Perform a case-sensitive search. By default, fd uses case-insensitive searches, unless the From 87caef9513326aa4b7507b17671339a0e8362138 Mon Sep 17 00:00:00 2001 From: David Peter Date: Sun, 22 Aug 2021 15:28:32 +0200 Subject: [PATCH 11/12] Use --locked for cargo, enable 'cargo fmt' check --- .github/workflows/CICD.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index c632cb0..fd8bd83 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -27,16 +27,22 @@ jobs: toolchain: ${{ env.MIN_SUPPORTED_RUST_VERSION }} default: true profile: minimal # minimal component installation (ie, no documentation) - components: clippy + components: clippy, rustfmt + - name: Ensure `cargo fmt` has been run + uses: actions-rs/cargo@v1 + with: + command: fmt + args: -- --check - name: Run clippy (on minimum supported rust version to prevent warnings we can't fix) uses: actions-rs/cargo@v1 with: command: clippy - args: --all-targets --all-features + args: --locked --all-targets --all-features - name: Run tests uses: actions-rs/cargo@v1 with: command: test + args: --locked build: name: ${{ matrix.job.os }} (${{ matrix.job.target }}) @@ -100,7 +106,7 @@ jobs: with: use-cross: ${{ matrix.job.use-cross }} command: build - args: --release --target=${{ matrix.job.target }} + args: --locked --release --target=${{ matrix.job.target }} - name: Strip debug information from executable id: strip @@ -142,8 +148,9 @@ jobs: id: test-options shell: bash run: | + # test only library unit tests and binary for arm-type targets unset CARGO_TEST_OPTIONS - unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--bin ${PROJECT_NAME}" ;; esac; + unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--lib --bin ${PROJECT_NAME}" ;; esac; echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS} - name: Run tests @@ -151,7 +158,7 @@ jobs: with: use-cross: ${{ matrix.job.use-cross }} command: test - args: --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}} + args: --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}} - name: Create tarball id: package From 3ba90dd7685a73923a3a872c4ffc8756a198d755 Mon Sep 17 00:00:00 2001 From: David Peter Date: Sun, 22 Aug 2021 15:51:58 +0200 Subject: [PATCH 12/12] Remove --lib option --- .github/workflows/CICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index fd8bd83..3bf1da4 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -150,7 +150,7 @@ jobs: run: | # test only library unit tests and binary for arm-type targets unset CARGO_TEST_OPTIONS - unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--lib --bin ${PROJECT_NAME}" ;; esac; + unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--bin ${PROJECT_NAME}" ;; esac; echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS} - name: Run tests