mirror of https://github.com/sharkdp/fd.git
Add command line argument for printing the executed command. No
highlighting yet.
This commit is contained in:
parent
1b71425419
commit
e1e4cc7fc1
|
@ -441,6 +441,15 @@ pub fn build_app() -> Command<'static> {
|
|||
"
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("print-exec")
|
||||
.long("print-exec")
|
||||
.short('P')
|
||||
.help("Print each command ran with -x or -X.")
|
||||
.long_help(
|
||||
"Print each command to be ran. If -x or -X is not used, this argument has no effect."
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("batch-size")
|
||||
.long("batch-size")
|
||||
|
|
|
@ -5,11 +5,13 @@ use std::{
|
|||
|
||||
use once_cell::unsync::OnceCell;
|
||||
|
||||
#[derive(Clone)]
|
||||
enum DirEntryInner {
|
||||
Normal(ignore::DirEntry),
|
||||
BrokenSymlink(PathBuf),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DirEntry {
|
||||
inner: DirEntryInner,
|
||||
metadata: OnceCell<Option<Metadata>>,
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use std::sync::mpsc::Receiver;
|
||||
use std::io::{Write, stdout};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::mpsc::{Receiver, channel};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::dir_entry::DirEntry;
|
||||
|
@ -6,7 +8,46 @@ use crate::error::print_error;
|
|||
use crate::exit_codes::{merge_exitcodes, ExitCode};
|
||||
use crate::walk::WorkerResult;
|
||||
|
||||
use super::CommandSet;
|
||||
use super::{CommandSet, CommandSetDisplay};
|
||||
|
||||
pub fn print_command<W: Write>(stdout: &mut W, path: &PathBuf, cmd: &CommandSet) {
|
||||
if let Err(e) = write!(stdout,"{}\n", CommandSetDisplay::new(cmd, path.to_path_buf())) {
|
||||
print_error(format!("Could not write to output: {}", e));
|
||||
ExitCode::GeneralError.exit();
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/// Print commands in the reciever without running them.
|
||||
/// Returns a new channel with the same results to allow passing the
|
||||
/// return value to the `job`-function to run the commands concurrently.
|
||||
pub fn peek_job_commands(
|
||||
rx: &Receiver<WorkerResult>,
|
||||
cmd: &CommandSet,
|
||||
show_filesystem_errors: bool,
|
||||
) -> Receiver<WorkerResult> {
|
||||
let (send, rx_new) = channel::<WorkerResult>();
|
||||
let res: Vec<PathBuf> = rx
|
||||
.into_iter()
|
||||
.filter_map(|worker_result| {
|
||||
send.send(worker_result.to_owned()).unwrap();
|
||||
match worker_result {
|
||||
WorkerResult::Entry(dir_entry) => {
|
||||
Some(dir_entry.into_path())
|
||||
},
|
||||
WorkerResult::Error(err) => {
|
||||
if show_filesystem_errors {
|
||||
print_error(err.to_string());
|
||||
}
|
||||
None
|
||||
}}
|
||||
})
|
||||
.collect();
|
||||
for p in res.iter() {
|
||||
print_command(&mut stdout(), p, cmd);
|
||||
}
|
||||
return rx_new
|
||||
}
|
||||
|
||||
/// An event loop that listens for inputs from the `rx` receiver. Each received input will
|
||||
/// generate a command with the supplied command template. The generated command will then
|
||||
|
@ -16,7 +57,7 @@ pub fn job(
|
|||
cmd: Arc<CommandSet>,
|
||||
out_perm: Arc<Mutex<()>>,
|
||||
show_filesystem_errors: bool,
|
||||
buffer_output: bool,
|
||||
buffer_output: bool
|
||||
) -> ExitCode {
|
||||
let mut results: Vec<ExitCode> = Vec::new();
|
||||
loop {
|
||||
|
@ -38,9 +79,11 @@ pub fn job(
|
|||
|
||||
// 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.execute(dir_entry.path(), Arc::clone(&out_perm), buffer_output))
|
||||
}
|
||||
|
||||
// Returns error in case of any error.
|
||||
merge_exitcodes(results)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ mod token;
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fmt::{Display, Debug};
|
||||
use std::io;
|
||||
use std::iter;
|
||||
use std::path::{Component, Path, PathBuf, Prefix};
|
||||
|
@ -20,7 +21,7 @@ use crate::exit_codes::ExitCode;
|
|||
|
||||
use self::command::{execute_commands, handle_cmd_error};
|
||||
use self::input::{basename, dirname, remove_extension};
|
||||
pub use self::job::{batch, job};
|
||||
pub use self::job::{batch, job, peek_job_commands};
|
||||
use self::token::Token;
|
||||
|
||||
/// Execution mode of the command
|
||||
|
@ -36,11 +37,35 @@ pub enum ExecutionMode {
|
|||
pub struct CommandSet {
|
||||
mode: ExecutionMode,
|
||||
path_separator: Option<String>,
|
||||
print: bool,
|
||||
commands: Vec<CommandTemplate>,
|
||||
}
|
||||
|
||||
// Wrapper for displaying Commands.
|
||||
pub struct CommandSetDisplay<'a> {
|
||||
command_set: &'a CommandSet,
|
||||
input: PathBuf
|
||||
}
|
||||
|
||||
impl <'a> CommandSetDisplay<'a> {
|
||||
pub fn new( command_set: &'a CommandSet, input: PathBuf ) -> CommandSetDisplay {
|
||||
CommandSetDisplay { command_set, input }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for CommandSetDisplay<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self
|
||||
.command_set.commands
|
||||
.iter()
|
||||
.for_each(|c| { write!(f, "{:?}", c.generate_string(&self.input, Some("/"))); return ()});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl CommandSet {
|
||||
pub fn new<I, S>(input: I, path_separator: Option<String>) -> Result<CommandSet>
|
||||
pub fn new<I, S>(input: I, path_separator: Option<String>, print: bool) -> Result<CommandSet>
|
||||
where
|
||||
I: IntoIterator<Item = Vec<S>>,
|
||||
S: AsRef<str>,
|
||||
|
@ -48,6 +73,7 @@ impl CommandSet {
|
|||
Ok(CommandSet {
|
||||
mode: ExecutionMode::OneByOne,
|
||||
path_separator,
|
||||
print,
|
||||
commands: input
|
||||
.into_iter()
|
||||
.map(CommandTemplate::new)
|
||||
|
@ -55,7 +81,7 @@ impl CommandSet {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn new_batch<I, S>(input: I, path_separator: Option<String>) -> Result<CommandSet>
|
||||
pub fn new_batch<I, S>(input: I, path_separator: Option<String>, print: bool) -> Result<CommandSet>
|
||||
where
|
||||
I: IntoIterator<Item = Vec<S>>,
|
||||
S: AsRef<str>,
|
||||
|
@ -63,6 +89,7 @@ impl CommandSet {
|
|||
Ok(CommandSet {
|
||||
mode: ExecutionMode::Batch,
|
||||
path_separator,
|
||||
print,
|
||||
commands: input
|
||||
.into_iter()
|
||||
.map(|args| {
|
||||
|
@ -83,6 +110,10 @@ impl CommandSet {
|
|||
self.mode == ExecutionMode::Batch
|
||||
}
|
||||
|
||||
pub fn should_print(&self) -> bool {
|
||||
self.print
|
||||
}
|
||||
|
||||
pub fn execute(&self, input: &Path, out_perm: Arc<Mutex<()>>, buffer_output: bool) -> ExitCode {
|
||||
let path_separator = self.path_separator.as_deref();
|
||||
let commands = self
|
||||
|
@ -215,6 +246,18 @@ struct CommandTemplate {
|
|||
args: Vec<ArgumentTemplate>,
|
||||
}
|
||||
|
||||
impl Display for CommandTemplate {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for arg in self.args.iter() {
|
||||
if let Err(e) = arg.fmt(f) {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl CommandTemplate {
|
||||
fn new<I, S>(input: I) -> Result<CommandTemplate>
|
||||
where
|
||||
|
@ -299,6 +342,15 @@ impl CommandTemplate {
|
|||
}
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
fn generate_string(&self, input: &Path, path_separator: Option<&str>) -> OsString {
|
||||
let mut res: OsString = self.args[0].generate(&input, path_separator);
|
||||
for arg in &self.args[1..] {
|
||||
res.push(" ");
|
||||
res.push(arg.generate(&input, path_separator));
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a template for a single command argument.
|
||||
|
@ -311,6 +363,22 @@ enum ArgumentTemplate {
|
|||
Text(String),
|
||||
}
|
||||
|
||||
//impl Display for ArgumentTemplate {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// match self {
|
||||
// Self::Tokens(tokens) => {
|
||||
// for token in tokens {
|
||||
// if let Err(e) = token.fmt(f) {
|
||||
// return Err(e)
|
||||
// }
|
||||
// }
|
||||
// Ok(())
|
||||
// },
|
||||
// Self::Text(text) => write!(f,"{}",text)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
impl ArgumentTemplate {
|
||||
pub fn has_tokens(&self) -> bool {
|
||||
matches!(self, ArgumentTemplate::Tokens(_))
|
||||
|
@ -413,7 +481,7 @@ mod tests {
|
|||
#[test]
|
||||
fn tokens_with_placeholder() {
|
||||
assert_eq!(
|
||||
CommandSet::new(vec![vec![&"echo", &"${SHELL}:"]], None).unwrap(),
|
||||
CommandSet::new(vec![vec![&"echo", &"${SHELL}:"]], None, false).unwrap(),
|
||||
CommandSet {
|
||||
commands: vec![CommandTemplate {
|
||||
args: vec![
|
||||
|
@ -422,6 +490,7 @@ mod tests {
|
|||
ArgumentTemplate::Tokens(vec![Token::Placeholder]),
|
||||
]
|
||||
}],
|
||||
print: false,
|
||||
mode: ExecutionMode::OneByOne,
|
||||
path_separator: None,
|
||||
}
|
||||
|
@ -431,7 +500,7 @@ mod tests {
|
|||
#[test]
|
||||
fn tokens_with_no_extension() {
|
||||
assert_eq!(
|
||||
CommandSet::new(vec![vec!["echo", "{.}"]], None).unwrap(),
|
||||
CommandSet::new(vec![vec!["echo", "{.}"]], None, false).unwrap(),
|
||||
CommandSet {
|
||||
commands: vec![CommandTemplate {
|
||||
args: vec![
|
||||
|
@ -439,6 +508,7 @@ mod tests {
|
|||
ArgumentTemplate::Tokens(vec![Token::NoExt]),
|
||||
],
|
||||
}],
|
||||
print: false,
|
||||
mode: ExecutionMode::OneByOne,
|
||||
path_separator: None,
|
||||
}
|
||||
|
@ -448,7 +518,7 @@ mod tests {
|
|||
#[test]
|
||||
fn tokens_with_basename() {
|
||||
assert_eq!(
|
||||
CommandSet::new(vec![vec!["echo", "{/}"]], None).unwrap(),
|
||||
CommandSet::new(vec![vec!["echo", "{/}"]], None,false).unwrap(),
|
||||
CommandSet {
|
||||
commands: vec![CommandTemplate {
|
||||
args: vec![
|
||||
|
@ -456,6 +526,7 @@ mod tests {
|
|||
ArgumentTemplate::Tokens(vec![Token::Basename]),
|
||||
],
|
||||
}],
|
||||
print: false,
|
||||
mode: ExecutionMode::OneByOne,
|
||||
path_separator: None,
|
||||
}
|
||||
|
@ -465,7 +536,7 @@ mod tests {
|
|||
#[test]
|
||||
fn tokens_with_parent() {
|
||||
assert_eq!(
|
||||
CommandSet::new(vec![vec!["echo", "{//}"]], None).unwrap(),
|
||||
CommandSet::new(vec![vec!["echo", "{//}"]], None,false).unwrap(),
|
||||
CommandSet {
|
||||
commands: vec![CommandTemplate {
|
||||
args: vec![
|
||||
|
@ -473,6 +544,7 @@ mod tests {
|
|||
ArgumentTemplate::Tokens(vec![Token::Parent]),
|
||||
],
|
||||
}],
|
||||
print: false,
|
||||
mode: ExecutionMode::OneByOne,
|
||||
path_separator: None,
|
||||
}
|
||||
|
@ -482,7 +554,7 @@ mod tests {
|
|||
#[test]
|
||||
fn tokens_with_basename_no_extension() {
|
||||
assert_eq!(
|
||||
CommandSet::new(vec![vec!["echo", "{/.}"]], None).unwrap(),
|
||||
CommandSet::new(vec![vec!["echo", "{/.}"]], None,false).unwrap(),
|
||||
CommandSet {
|
||||
commands: vec![CommandTemplate {
|
||||
args: vec![
|
||||
|
@ -490,6 +562,7 @@ mod tests {
|
|||
ArgumentTemplate::Tokens(vec![Token::BasenameNoExt]),
|
||||
],
|
||||
}],
|
||||
print: false,
|
||||
mode: ExecutionMode::OneByOne,
|
||||
path_separator: None,
|
||||
}
|
||||
|
@ -499,7 +572,7 @@ mod tests {
|
|||
#[test]
|
||||
fn tokens_multiple() {
|
||||
assert_eq!(
|
||||
CommandSet::new(vec![vec!["cp", "{}", "{/.}.ext"]], None).unwrap(),
|
||||
CommandSet::new(vec![vec!["cp", "{}", "{/.}.ext"]], None,false).unwrap(),
|
||||
CommandSet {
|
||||
commands: vec![CommandTemplate {
|
||||
args: vec![
|
||||
|
@ -511,6 +584,7 @@ mod tests {
|
|||
]),
|
||||
],
|
||||
}],
|
||||
print: false,
|
||||
mode: ExecutionMode::OneByOne,
|
||||
path_separator: None,
|
||||
}
|
||||
|
@ -520,7 +594,7 @@ mod tests {
|
|||
#[test]
|
||||
fn tokens_single_batch() {
|
||||
assert_eq!(
|
||||
CommandSet::new_batch(vec![vec!["echo", "{.}"]], None).unwrap(),
|
||||
CommandSet::new_batch(vec![vec!["echo", "{.}"]], None,false).unwrap(),
|
||||
CommandSet {
|
||||
commands: vec![CommandTemplate {
|
||||
args: vec![
|
||||
|
@ -528,6 +602,7 @@ mod tests {
|
|||
ArgumentTemplate::Tokens(vec![Token::NoExt]),
|
||||
],
|
||||
}],
|
||||
print: false,
|
||||
mode: ExecutionMode::Batch,
|
||||
path_separator: None,
|
||||
}
|
||||
|
@ -536,7 +611,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn tokens_multiple_batch() {
|
||||
assert!(CommandSet::new_batch(vec![vec!["echo", "{.}", "{}"]], None).is_err());
|
||||
assert!(CommandSet::new_batch(vec![vec!["echo", "{.}", "{}"]], None,false).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -546,7 +621,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn command_set_no_args() {
|
||||
assert!(CommandSet::new(vec![vec!["echo"], vec![]], None).is_err());
|
||||
assert!(CommandSet::new(vec![vec!["echo"], vec![]], None,false).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -399,12 +399,12 @@ fn extract_command(
|
|||
None.or_else(|| {
|
||||
matches
|
||||
.grouped_values_of("exec")
|
||||
.map(|args| CommandSet::new(args, path_separator.map(str::to_string)))
|
||||
.map(|args| CommandSet::new(args, path_separator.map(str::to_string), matches.is_present("print-exec")))
|
||||
})
|
||||
.or_else(|| {
|
||||
matches
|
||||
.grouped_values_of("exec-batch")
|
||||
.map(|args| CommandSet::new_batch(args, path_separator.map(str::to_string)))
|
||||
.map(|args| CommandSet::new_batch(args, path_separator.map(str::to_string), matches.is_present("print-exec")))
|
||||
})
|
||||
.or_else(|| {
|
||||
if !matches.is_present("list-details") {
|
||||
|
@ -415,7 +415,7 @@ fn extract_command(
|
|||
let color_arg = format!("--color={}", color);
|
||||
|
||||
let res = determine_ls_command(&color_arg, colored_output)
|
||||
.map(|cmd| CommandSet::new_batch([cmd], path_separator.map(str::to_string)).unwrap());
|
||||
.map(|cmd| CommandSet::new_batch([cmd], path_separator.map(str::to_string),false).unwrap());
|
||||
|
||||
Some(res)
|
||||
})
|
||||
|
|
10
src/walk.rs
10
src/walk.rs
|
@ -34,6 +34,7 @@ enum ReceiverMode {
|
|||
}
|
||||
|
||||
/// The Worker threads can result in a valid entry having PathBuf or an error.
|
||||
#[derive(Clone)]
|
||||
pub enum WorkerResult {
|
||||
Entry(DirEntry),
|
||||
Error(ignore::Error),
|
||||
|
@ -351,7 +352,12 @@ fn spawn_receiver(
|
|||
if cmd.in_batch_mode() {
|
||||
exec::batch(rx, cmd, show_filesystem_errors, config.batch_size)
|
||||
} else {
|
||||
let shared_rx = Arc::new(Mutex::new(rx));
|
||||
|
||||
let shared_rx = if cmd.should_print() {
|
||||
Arc::new(Mutex::new( exec::peek_job_commands(&rx, cmd, show_filesystem_errors) ))
|
||||
} else {
|
||||
Arc::new(Mutex::new(rx))
|
||||
};
|
||||
|
||||
let out_perm = Arc::new(Mutex::new(()));
|
||||
|
||||
|
@ -369,7 +375,7 @@ fn spawn_receiver(
|
|||
cmd,
|
||||
out_perm,
|
||||
show_filesystem_errors,
|
||||
enable_output_buffering,
|
||||
enable_output_buffering
|
||||
)
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue