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>>>,
base: Arc<Option<PathBuf>>,
cmd: Arc<TokenizedCommand>,
out_perm: Arc<Mutex<()>>,
) {
// A string buffer that will be re-used in each iteration.
let buffer = &mut String::with_capacity(256);
@ -35,6 +36,7 @@ pub fn job(
drop(lock);
// Generate a command to store within the buffer, and execute the command.
// 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;
use std::path::Path;
use std::sync::{Arc, Mutex};
use self::paths::{basename, dirname, remove_extension};
use self::ticket::CommandTicket;
@ -102,7 +103,12 @@ impl TokenizedCommand {
/// 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
/// 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 {
match *token {
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::process::Command;
use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex};
use std::io::{self, Write};
lazy_static! {
/// 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.
pub struct CommandTicket<'a> {
command: &'a mut String,
out_perm: Arc<Mutex<()>>,
}
impl<'a> CommandTicket<'a> {
pub fn new(command: &'a mut String) -> CommandTicket<'a> {
CommandTicket { command }
pub fn new(command: &'a mut String, out_perm: Arc<Mutex<()>>) -> CommandTicket<'a> {
CommandTicket { command, out_perm }
}
/// Executes the command stored within the ticket, and
@ -32,12 +35,22 @@ impl<'a> CommandTicket<'a> {
let cmd = Command::new(COMMAND.0.as_str())
.arg(COMMAND.1)
.arg(&self.command)
.spawn();
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output();
// Then wait for the command to exit, if it was spawned.
match cmd {
Ok(mut child) => {
let _ = child.wait();
Ok(output) => {
// 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),
}

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.
if let Some(ref cmd) = rx_config.command {
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 });
// 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 cmd = cmd.clone();
let base = base.clone();
let out_perm = out_perm.clone();
// 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.
handles.push(handle);