mirror of
https://github.com/sharkdp/fd.git
synced 2024-11-19 02:10:34 +01:00
parent
5039d2db99
commit
4ffc34956f
7 changed files with 66 additions and 44 deletions
|
@ -724,8 +724,10 @@ pub fn build_app() -> Command<'static> {
|
||||||
.hide_short_help(true)
|
.hide_short_help(true)
|
||||||
.help("strip './' prefix from -0/--print0 output")
|
.help("strip './' prefix from -0/--print0 output")
|
||||||
.long_help(
|
.long_help(
|
||||||
"By default, relative paths are prefixed with './' when -0/--print0 is given, to \
|
"By default, relative paths are prefixed with './' when -x/--exec, \
|
||||||
make them safer for use with xargs. Use this flag to disable this behaviour."
|
-X/--exec-batch, or -0/--print0 are given, to reduce the risk of a \
|
||||||
|
path starting with '-' being treated as a command line option. Use \
|
||||||
|
this flag to disable this behaviour."
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,9 @@ use std::{
|
||||||
|
|
||||||
use once_cell::unsync::OnceCell;
|
use once_cell::unsync::OnceCell;
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::filesystem::strip_current_dir;
|
||||||
|
|
||||||
enum DirEntryInner {
|
enum DirEntryInner {
|
||||||
Normal(ignore::DirEntry),
|
Normal(ignore::DirEntry),
|
||||||
BrokenSymlink(PathBuf),
|
BrokenSymlink(PathBuf),
|
||||||
|
@ -45,6 +48,24 @@ impl DirEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the path as it should be presented to the user.
|
||||||
|
pub fn stripped_path(&self, config: &Config) -> &Path {
|
||||||
|
if config.strip_cwd_prefix {
|
||||||
|
strip_current_dir(self.path())
|
||||||
|
} else {
|
||||||
|
self.path()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the path as it should be presented to the user.
|
||||||
|
pub fn into_stripped_path(self, config: &Config) -> PathBuf {
|
||||||
|
if config.strip_cwd_prefix {
|
||||||
|
self.stripped_path(config).to_path_buf()
|
||||||
|
} else {
|
||||||
|
self.into_path()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn file_type(&self) -> Option<FileType> {
|
pub fn file_type(&self) -> Option<FileType> {
|
||||||
match &self.inner {
|
match &self.inner {
|
||||||
DirEntryInner::Normal(e) => e.file_type(),
|
DirEntryInner::Normal(e) => e.file_type(),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
use crate::dir_entry::DirEntry;
|
use crate::dir_entry::DirEntry;
|
||||||
use crate::error::print_error;
|
use crate::error::print_error;
|
||||||
use crate::exit_codes::{merge_exitcodes, ExitCode};
|
use crate::exit_codes::{merge_exitcodes, ExitCode};
|
||||||
|
@ -15,9 +16,11 @@ pub fn job(
|
||||||
rx: Arc<Mutex<Receiver<WorkerResult>>>,
|
rx: Arc<Mutex<Receiver<WorkerResult>>>,
|
||||||
cmd: Arc<CommandSet>,
|
cmd: Arc<CommandSet>,
|
||||||
out_perm: Arc<Mutex<()>>,
|
out_perm: Arc<Mutex<()>>,
|
||||||
show_filesystem_errors: bool,
|
config: &Config,
|
||||||
buffer_output: bool,
|
|
||||||
) -> ExitCode {
|
) -> ExitCode {
|
||||||
|
// Output should be buffered when only running a single thread
|
||||||
|
let buffer_output: bool = config.threads > 1;
|
||||||
|
|
||||||
let mut results: Vec<ExitCode> = Vec::new();
|
let mut results: Vec<ExitCode> = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
// Create a lock on the shared receiver for this thread.
|
// Create a lock on the shared receiver for this thread.
|
||||||
|
@ -28,7 +31,7 @@ pub fn job(
|
||||||
let dir_entry: DirEntry = match lock.recv() {
|
let dir_entry: DirEntry = match lock.recv() {
|
||||||
Ok(WorkerResult::Entry(dir_entry)) => dir_entry,
|
Ok(WorkerResult::Entry(dir_entry)) => dir_entry,
|
||||||
Ok(WorkerResult::Error(err)) => {
|
Ok(WorkerResult::Error(err)) => {
|
||||||
if show_filesystem_errors {
|
if config.show_filesystem_errors {
|
||||||
print_error(err.to_string());
|
print_error(err.to_string());
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -39,29 +42,28 @@ pub fn job(
|
||||||
// Drop the lock so that other threads can read from the receiver.
|
// Drop the lock so that other threads can read from the receiver.
|
||||||
drop(lock);
|
drop(lock);
|
||||||
// Generate a command, execute it and store its exit code.
|
// Generate a command, execute it and store its exit code.
|
||||||
results.push(cmd.execute(dir_entry.path(), Arc::clone(&out_perm), buffer_output))
|
results.push(cmd.execute(
|
||||||
|
dir_entry.stripped_path(config),
|
||||||
|
Arc::clone(&out_perm),
|
||||||
|
buffer_output,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
// Returns error in case of any error.
|
// Returns error in case of any error.
|
||||||
merge_exitcodes(results)
|
merge_exitcodes(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn batch(
|
pub fn batch(rx: Receiver<WorkerResult>, cmd: &CommandSet, config: &Config) -> ExitCode {
|
||||||
rx: Receiver<WorkerResult>,
|
|
||||||
cmd: &CommandSet,
|
|
||||||
show_filesystem_errors: bool,
|
|
||||||
limit: usize,
|
|
||||||
) -> ExitCode {
|
|
||||||
let paths = rx
|
let paths = rx
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|worker_result| match worker_result {
|
.filter_map(|worker_result| match worker_result {
|
||||||
WorkerResult::Entry(dir_entry) => Some(dir_entry.into_path()),
|
WorkerResult::Entry(dir_entry) => Some(dir_entry.into_stripped_path(config)),
|
||||||
WorkerResult::Error(err) => {
|
WorkerResult::Error(err) => {
|
||||||
if show_filesystem_errors {
|
if config.show_filesystem_errors {
|
||||||
print_error(err.to_string());
|
print_error(err.to_string());
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cmd.execute_batch(paths, limit)
|
cmd.execute_batch(paths, config.batch_size)
|
||||||
}
|
}
|
||||||
|
|
|
@ -383,9 +383,12 @@ fn construct_config(matches: clap::ArgMatches, pattern_regex: &str) -> Result<Co
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
strip_cwd_prefix: (!matches.is_present("path")
|
strip_cwd_prefix: !matches.is_present("path")
|
||||||
&& !matches.is_present("search-path")
|
&& !matches.is_present("search-path")
|
||||||
&& (!matches.is_present("null_separator") || matches.is_present("strip-cwd-prefix"))),
|
&& (matches.is_present("strip-cwd-prefix")
|
||||||
|
|| !(matches.is_present("null_separator")
|
||||||
|
|| matches.is_present("exec")
|
||||||
|
|| matches.is_present("exec-batch"))),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use lscolors::{Indicator, LsColors, Style};
|
use lscolors::{Indicator, LsColors, Style};
|
||||||
|
|
||||||
|
@ -8,21 +7,11 @@ use crate::config::Config;
|
||||||
use crate::dir_entry::DirEntry;
|
use crate::dir_entry::DirEntry;
|
||||||
use crate::error::print_error;
|
use crate::error::print_error;
|
||||||
use crate::exit_codes::ExitCode;
|
use crate::exit_codes::ExitCode;
|
||||||
use crate::filesystem::strip_current_dir;
|
|
||||||
|
|
||||||
fn replace_path_separator(path: &str, new_path_separator: &str) -> String {
|
fn replace_path_separator(path: &str, new_path_separator: &str) -> String {
|
||||||
path.replace(std::path::MAIN_SEPARATOR, new_path_separator)
|
path.replace(std::path::MAIN_SEPARATOR, new_path_separator)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stripped_path<'a>(entry: &'a DirEntry, config: &Config) -> &'a Path {
|
|
||||||
let path = entry.path();
|
|
||||||
if config.strip_cwd_prefix {
|
|
||||||
strip_current_dir(path)
|
|
||||||
} else {
|
|
||||||
path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: this function is performance critical and can probably be optimized
|
// TODO: this function is performance critical and can probably be optimized
|
||||||
pub fn print_entry<W: Write>(stdout: &mut W, entry: &DirEntry, config: &Config) {
|
pub fn print_entry<W: Write>(stdout: &mut W, entry: &DirEntry, config: &Config) {
|
||||||
let r = if let Some(ref ls_colors) = config.ls_colors {
|
let r = if let Some(ref ls_colors) = config.ls_colors {
|
||||||
|
@ -74,7 +63,7 @@ fn print_entry_colorized<W: Write>(
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
// Split the path between the parent and the last component
|
// Split the path between the parent and the last component
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
let path = stripped_path(entry, config);
|
let path = entry.stripped_path(config);
|
||||||
let path_str = path.to_string_lossy();
|
let path_str = path.to_string_lossy();
|
||||||
|
|
||||||
if let Some(parent) = path.parent() {
|
if let Some(parent) = path.parent() {
|
||||||
|
@ -130,7 +119,7 @@ fn print_entry_uncolorized_base<W: Write>(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
let separator = if config.null_separator { "\0" } else { "\n" };
|
let separator = if config.null_separator { "\0" } else { "\n" };
|
||||||
let path = stripped_path(entry, config);
|
let path = entry.stripped_path(config);
|
||||||
|
|
||||||
let mut path_string = path.to_string_lossy();
|
let mut path_string = path.to_string_lossy();
|
||||||
if let Some(ref separator) = config.path_separator {
|
if let Some(ref separator) = config.path_separator {
|
||||||
|
@ -164,7 +153,7 @@ fn print_entry_uncolorized<W: Write>(
|
||||||
} else {
|
} else {
|
||||||
// Print path as raw bytes, allowing invalid UTF-8 filenames to be passed to other processes
|
// Print path as raw bytes, allowing invalid UTF-8 filenames to be passed to other processes
|
||||||
let separator = if config.null_separator { b"\0" } else { b"\n" };
|
let separator = if config.null_separator { b"\0" } else { b"\n" };
|
||||||
stdout.write_all(stripped_path(entry, config).as_os_str().as_bytes())?;
|
stdout.write_all(entry.stripped_path(config).as_os_str().as_bytes())?;
|
||||||
print_trailing_slash(stdout, entry, config, None)?;
|
print_trailing_slash(stdout, entry, config, None)?;
|
||||||
stdout.write_all(separator)
|
stdout.write_all(separator)
|
||||||
}
|
}
|
||||||
|
|
16
src/walk.rs
16
src/walk.rs
|
@ -341,15 +341,12 @@ fn spawn_receiver(
|
||||||
let quit_flag = Arc::clone(quit_flag);
|
let quit_flag = Arc::clone(quit_flag);
|
||||||
let interrupt_flag = Arc::clone(interrupt_flag);
|
let interrupt_flag = Arc::clone(interrupt_flag);
|
||||||
|
|
||||||
let show_filesystem_errors = config.show_filesystem_errors;
|
|
||||||
let threads = config.threads;
|
let threads = config.threads;
|
||||||
// This will be used to check if output should be buffered when only running a single thread
|
|
||||||
let enable_output_buffering: bool = threads > 1;
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
// This will be set to `Some` if the `--exec` argument was supplied.
|
// This will be set to `Some` if the `--exec` argument was supplied.
|
||||||
if let Some(ref cmd) = config.command {
|
if let Some(ref cmd) = config.command {
|
||||||
if cmd.in_batch_mode() {
|
if cmd.in_batch_mode() {
|
||||||
exec::batch(rx, cmd, show_filesystem_errors, config.batch_size)
|
exec::batch(rx, cmd, &config)
|
||||||
} else {
|
} else {
|
||||||
let shared_rx = Arc::new(Mutex::new(rx));
|
let shared_rx = Arc::new(Mutex::new(rx));
|
||||||
|
|
||||||
|
@ -358,20 +355,13 @@ fn spawn_receiver(
|
||||||
// Each spawned job will store it's thread handle in here.
|
// Each spawned job will store it's thread handle in here.
|
||||||
let mut handles = Vec::with_capacity(threads);
|
let mut handles = Vec::with_capacity(threads);
|
||||||
for _ in 0..threads {
|
for _ in 0..threads {
|
||||||
|
let config = Arc::clone(&config);
|
||||||
let rx = Arc::clone(&shared_rx);
|
let rx = Arc::clone(&shared_rx);
|
||||||
let cmd = Arc::clone(cmd);
|
let cmd = Arc::clone(cmd);
|
||||||
let out_perm = Arc::clone(&out_perm);
|
let out_perm = Arc::clone(&out_perm);
|
||||||
|
|
||||||
// Spawn a job thread that will listen for and execute inputs.
|
// Spawn a job thread that will listen for and execute inputs.
|
||||||
let handle = thread::spawn(move || {
|
let handle = thread::spawn(move || exec::job(rx, cmd, out_perm, &config));
|
||||||
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.
|
// Push the handle of the spawned thread into the vector for later joining.
|
||||||
handles.push(handle);
|
handles.push(handle);
|
||||||
|
|
|
@ -1327,6 +1327,16 @@ fn test_exec() {
|
||||||
./one/two/three/directory_foo",
|
./one/two/three/directory_foo",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
te.assert_output(
|
||||||
|
&["foo", "--strip-cwd-prefix", "--exec", "echo", "{}"],
|
||||||
|
"a.foo
|
||||||
|
one/b.foo
|
||||||
|
one/two/C.Foo2
|
||||||
|
one/two/c.foo
|
||||||
|
one/two/three/d.foo
|
||||||
|
one/two/three/directory_foo",
|
||||||
|
);
|
||||||
|
|
||||||
te.assert_output(
|
te.assert_output(
|
||||||
&["foo", "--exec", "echo", "{.}"],
|
&["foo", "--exec", "echo", "{.}"],
|
||||||
"a
|
"a
|
||||||
|
@ -1452,6 +1462,11 @@ fn test_exec_batch() {
|
||||||
"./a.foo ./one/b.foo ./one/two/C.Foo2 ./one/two/c.foo ./one/two/three/d.foo ./one/two/three/directory_foo",
|
"./a.foo ./one/b.foo ./one/two/C.Foo2 ./one/two/c.foo ./one/two/three/d.foo ./one/two/three/directory_foo",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
te.assert_output(
|
||||||
|
&["foo", "--strip-cwd-prefix", "--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",
|
||||||
|
);
|
||||||
|
|
||||||
te.assert_output(
|
te.assert_output(
|
||||||
&["foo", "--exec-batch", "echo", "{/}"],
|
&["foo", "--exec-batch", "echo", "{/}"],
|
||||||
"a.foo b.foo C.Foo2 c.foo d.foo directory_foo",
|
"a.foo b.foo C.Foo2 c.foo d.foo directory_foo",
|
||||||
|
|
Loading…
Reference in a new issue