Group outputs from exec

This commit is contained in:
Michael Aaron Murphy 2017-10-14 17:59:36 -04:00
parent 718f723d31
commit 1bc58b2fbb
4 changed files with 35 additions and 10 deletions

View file

@ -11,6 +11,7 @@ pub fn job(
rx: Arc<Mutex<Receiver<PathBuf>>>, rx: Arc<Mutex<Receiver<PathBuf>>>,
base: Arc<Option<PathBuf>>, base: Arc<Option<PathBuf>>,
cmd: Arc<TokenizedCommand>, cmd: Arc<TokenizedCommand>,
out_perm: Arc<Mutex<()>>,
) { ) {
// A string buffer that will be re-used in each iteration. // A string buffer that will be re-used in each iteration.
let buffer = &mut String::with_capacity(256); let buffer = &mut String::with_capacity(256);
@ -35,6 +36,7 @@ pub fn job(
drop(lock); drop(lock);
// Generate a command to store within the buffer, and execute the command. // Generate a command to store within the buffer, and execute the command.
// Note that the `then_execute()` method will clear the buffer for us. // Note that the `then_execute()` method will clear the buffer for us.
cmd.generate(buffer, &value).then_execute(); cmd.generate(buffer, &value, out_perm.clone())
.then_execute();
} }
} }

View file

@ -5,6 +5,7 @@ mod job;
mod paths; mod paths;
use std::path::Path; use std::path::Path;
use std::sync::{Arc, Mutex};
use self::paths::{basename, dirname, remove_extension}; use self::paths::{basename, dirname, remove_extension};
use self::ticket::CommandTicket; use self::ticket::CommandTicket;
@ -102,7 +103,12 @@ impl TokenizedCommand {
/// written into the `command` buffer. Once all tokens have been processed, the mutable /// written into the `command` buffer. Once all tokens have been processed, the mutable
/// reference of the `command` will be wrapped within a `CommandTicket`, which will be /// reference of the `command` will be wrapped within a `CommandTicket`, which will be
/// responsible for executing the command and clearing the buffer. /// responsible for executing the command and clearing the buffer.
pub fn generate<'a>(&self, command: &'a mut String, input: &Path) -> CommandTicket<'a> { pub fn generate<'a>(
&self,
command: &'a mut String,
input: &Path,
out_perm: Arc<Mutex<()>>,
) -> CommandTicket<'a> {
for token in &self.tokens { for token in &self.tokens {
match *token { match *token {
Token::Basename => *command += basename(&input.to_string_lossy()), Token::Basename => *command += basename(&input.to_string_lossy()),
@ -116,7 +122,7 @@ impl TokenizedCommand {
} }
} }
CommandTicket::new(command) CommandTicket::new(command, out_perm)
} }
} }

View file

@ -1,5 +1,7 @@
use std::env; use std::env;
use std::process::Command; use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex};
use std::io::{self, Write};
lazy_static! { lazy_static! {
/// On non-Windows systems, the `SHELL` environment variable will be used to determine the /// On non-Windows systems, the `SHELL` environment variable will be used to determine the
@ -18,11 +20,12 @@ lazy_static! {
/// cleared so that a new command can be written to the string in the future. /// cleared so that a new command can be written to the string in the future.
pub struct CommandTicket<'a> { pub struct CommandTicket<'a> {
command: &'a mut String, command: &'a mut String,
out_perm: Arc<Mutex<()>>,
} }
impl<'a> CommandTicket<'a> { impl<'a> CommandTicket<'a> {
pub fn new(command: &'a mut String) -> CommandTicket<'a> { pub fn new(command: &'a mut String, out_perm: Arc<Mutex<()>>) -> CommandTicket<'a> {
CommandTicket { command } CommandTicket { command, out_perm }
} }
/// Executes the command stored within the ticket, and /// Executes the command stored within the ticket, and
@ -32,12 +35,22 @@ impl<'a> CommandTicket<'a> {
let cmd = Command::new(COMMAND.0.as_str()) let cmd = Command::new(COMMAND.0.as_str())
.arg(COMMAND.1) .arg(COMMAND.1)
.arg(&self.command) .arg(&self.command)
.spawn(); .stdout(Stdio::piped())
.stderr(Stdio::piped())
.output();
// Then wait for the command to exit, if it was spawned. // Then wait for the command to exit, if it was spawned.
match cmd { match cmd {
Ok(mut child) => { Ok(output) => {
let _ = child.wait(); // While this lock is active, this thread will be the only thread allowed
// to write it's outputs.
let _lock = self.out_perm.lock().unwrap();
let stdout = io::stdout();
let stderr = io::stderr();
let _ = stdout.lock().write_all(&output.stdout);
let _ = stderr.lock().write_all(&output.stderr);
} }
Err(why) => eprintln!("fd: exec error: {}", why), Err(why) => eprintln!("fd: exec error: {}", why),
} }

View file

@ -60,6 +60,9 @@ pub fn scan(root: &Path, pattern: Arc<Regex>, base: &Path, config: Arc<FdOptions
// 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) = rx_config.command { if let Some(ref cmd) = rx_config.command {
let shared_rx = Arc::new(Mutex::new(rx)); let shared_rx = Arc::new(Mutex::new(rx));
let out_perm = Arc::new(Mutex::new(()));
let base = Arc::new(if is_absolute { Some(rx_base) } else { None }); let base = Arc::new(if is_absolute { Some(rx_base) } else { None });
// This is safe because `cmd` will exist beyond the end of this scope. // This is safe because `cmd` will exist beyond the end of this scope.
@ -72,9 +75,10 @@ pub fn scan(root: &Path, pattern: Arc<Regex>, base: &Path, config: Arc<FdOptions
let rx = shared_rx.clone(); let rx = shared_rx.clone();
let cmd = cmd.clone(); let cmd = cmd.clone();
let base = base.clone(); let base = base.clone();
let out_perm = out_perm.clone();
// 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 || exec::job(rx, base, cmd)); let handle = thread::spawn(move || exec::job(rx, base, cmd, out_perm));
// 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);