-
Notifications
You must be signed in to change notification settings - Fork 23
Implement e (execute) command
#481
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
5f02eee
20df561
c923a2d
8a6e5bb
0513b13
11f1024
e70e5a9
25bb533
31fd8b0
fecf889
bc21770
a3bc1ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,7 @@ use crate::sed::named_writer; | |
|
|
||
| use std::borrow::Cow; | ||
| use std::cell::RefCell; | ||
| use std::io::{self, IsTerminal}; | ||
| use std::io::{self, IsTerminal, Read}; | ||
| use std::path::PathBuf; | ||
| use std::rc::Rc; | ||
| use uucore::display::Quotable; | ||
|
|
@@ -215,6 +215,49 @@ fn shell_command(_cmd: &str) -> std::process::Command { | |
| unimplemented!("the 'e' substitute flag requires a platform shell (/bin/sh or cmd.exe)"); | ||
| } | ||
|
|
||
| /// Run `cmd` in a shell, returning its standard output. The child's | ||
| /// standard error is left connected to this process's own, matching GNU | ||
| /// sed's behavior of letting shell errors surface directly rather than | ||
| /// being silently captured. | ||
| fn shell_stdout(cmd: &str) -> io::Result<Vec<u8>> { | ||
| let mut child = shell_command(cmd) | ||
| .stdout(std::process::Stdio::piped()) | ||
| .spawn()?; | ||
| let mut stdout = child.stdout.take().expect("stdout was piped"); | ||
| let mut buf = Vec::new(); | ||
| stdout.read_to_end(&mut buf)?; | ||
| child.wait()?; | ||
| Ok(buf) | ||
| } | ||
|
|
||
| /// Execute the pattern space as a shell command, replacing its contents | ||
| /// with the command's standard output, minus one trailing newline. | ||
| fn execute_pattern_as_shell( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better names |
||
| pattern: &mut IOChunk, | ||
| command: &Command, | ||
| context: &mut ProcessingContext, | ||
| ) -> UResult<()> { | ||
| let cmd_str = pattern.as_str()?.to_string(); | ||
| let stdout_bytes = shell_stdout(&cmd_str).map_err(|e| { | ||
| input_runtime_error::<()>( | ||
| &command.location, | ||
| context, | ||
| format!("failed to execute shell command: {e}"), | ||
| ) | ||
| .unwrap_err() | ||
| })?; | ||
| let mut shell_out = String::from_utf8_lossy(&stdout_bytes).into_owned(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As sed is a data processing tool rather than a UI front-end, we should avoid |
||
| if shell_out.ends_with("\r\n") { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make this active only for |
||
| // On windows, both return carriage and newline characters are used | ||
| shell_out.truncate(shell_out.len() - 2); | ||
| } else if shell_out.ends_with('\n') { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But keep this also for Windows, because some Windows tools use \n. |
||
| // Strip the trailing newline, as GNU sed does | ||
| shell_out.pop(); | ||
| } | ||
| pattern.set_to_string(shell_out, pattern.is_newline_terminated()); | ||
| Ok(()) | ||
| } | ||
|
|
||
| /// Perform the specified RE replacement in the provided pattern space. | ||
| fn substitute( | ||
| pattern: &mut IOChunk, | ||
|
|
@@ -328,24 +371,7 @@ fn substitute( | |
|
|
||
| // Execute the pattern space as a shell command if the 'e' flag is set | ||
| if sub.execute { | ||
| let cmd_str = pattern.as_str()?.to_string(); | ||
| let output_bytes = shell_command(&cmd_str).output().map_err(|e| { | ||
| input_runtime_error::<()>( | ||
| &command.location, | ||
| context, | ||
| format!("failed to execute shell command: {e}"), | ||
| ) | ||
| .unwrap_err() | ||
| })?; | ||
| let mut shell_out = String::from_utf8_lossy(&output_bytes.stdout).into_owned(); | ||
| if shell_out.ends_with("\r\n") { | ||
| // On windows, both return carriage and newline characters are used | ||
| shell_out.truncate(shell_out.len() - 2); | ||
| } else if shell_out.ends_with('\n') { | ||
| // Strip the trailing newline, as GNU sed does | ||
| shell_out.pop(); | ||
| } | ||
| pattern.set_to_string(shell_out, pattern.is_newline_terminated()); | ||
| execute_pattern_as_shell(pattern, command, context)?; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great reuse! |
||
| } | ||
|
|
||
| if sub.print_flag { | ||
|
|
@@ -581,6 +607,23 @@ fn process_file( | |
| pattern.clear(); | ||
| break; | ||
| } | ||
| 'e' => match &command.data { | ||
| CommandData::None => { | ||
| execute_pattern_as_shell(&mut pattern, &command, context)?; | ||
| } | ||
| CommandData::Text(cmd_str) => { | ||
| let stdout_bytes = shell_stdout(cmd_str).map_err(|e| { | ||
| input_runtime_error::<()>( | ||
| &command.location, | ||
| context, | ||
| format!("failed to execute shell command: {e}"), | ||
| ) | ||
| .unwrap_err() | ||
| })?; | ||
| output.write_str(String::from_utf8_lossy(&stdout_bytes).into_owned())?; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please avoid |
||
| } | ||
| _ => panic!("invalid 'e' command data"), | ||
| }, | ||
| 'g' => { | ||
| // Replace pattern with the contents of the hold space. | ||
| pattern.set_to_string(context.hold.content.clone(), context.hold.has_newline); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.