@@ -28,6 +28,7 @@ mod options {
2828 pub const NO_RUN_IF_EMPTY : & str = "no-run-if-empty" ;
2929 pub const NULL : & str = "null" ;
3030 pub const SIZE : & str = "size" ;
31+ pub const REPLACE : & str = "replace" ;
3132 pub const VERBOSE : & str = "verbose" ;
3233}
3334
@@ -40,6 +41,7 @@ struct Options {
4041 no_run_if_empty : bool ,
4142 null : bool ,
4243 size : Option < usize > ,
44+ replace : Option < String > ,
4345 verbose : bool ,
4446}
4547
@@ -340,13 +342,15 @@ struct CommandBuilderOptions {
340342 limiters : LimiterCollection ,
341343 verbose : bool ,
342344 close_stdin : bool ,
345+ replace : Option < String > ,
343346}
344347
345348impl CommandBuilderOptions {
346349 fn new (
347350 action : ExecAction ,
348351 env : HashMap < OsString , OsString > ,
349352 mut limiters : LimiterCollection ,
353+ replace : Option < String > ,
350354 ) -> Result < Self , ExhaustedCommandSpace > {
351355 let initial_args = match & action {
352356 ExecAction :: Command ( args) => args. iter ( ) . map ( |arg| arg. as_ref ( ) ) . collect ( ) ,
@@ -366,6 +370,7 @@ impl CommandBuilderOptions {
366370 limiters,
367371 verbose : false ,
368372 close_stdin : false ,
373+ replace,
369374 } )
370375 }
371376}
@@ -396,16 +401,42 @@ impl CommandBuilder<'_> {
396401 ExecAction :: Command ( args) => ( & args[ 0 ] , & args[ 1 ..] ) ,
397402 ExecAction :: Echo => ( OsStr :: new ( "echo" ) , & [ ] ) ,
398403 } ;
399-
400404 let mut command = Command :: new ( entry_point) ;
401- command
402- . args ( initial_args)
403- . args ( & self . extra_args )
404- . env_clear ( )
405- . envs ( & self . options . env ) ;
405+
406+ if let Some ( replace_str) = & self . options . replace {
407+ // we replace the first instance of the replacement string with
408+ // the extra args, and then replace all instances of the replacement
409+ let replacement = self
410+ . extra_args
411+ . iter ( )
412+ . map ( |s| s. to_string_lossy ( ) )
413+ . collect :: < Vec < _ > > ( )
414+ . join ( " " ) ;
415+ let initial_args: Vec < OsString > = initial_args
416+ . iter ( )
417+ . map ( |arg| {
418+ let arg_str = arg. to_string_lossy ( ) ;
419+ OsString :: from ( arg_str. replace ( replace_str, & replacement) )
420+ } )
421+ . collect ( ) ;
422+
423+ command
424+ . args ( & initial_args)
425+ . env_clear ( )
426+ . envs ( & self . options . env ) ;
427+ } else {
428+ // don't do any replacement
429+ command
430+ . args ( initial_args)
431+ . args ( & self . extra_args )
432+ . env_clear ( )
433+ . envs ( & self . options . env ) ;
434+ } ;
435+
406436 if self . options . close_stdin {
407437 command. stdin ( Stdio :: null ( ) ) ;
408438 }
439+
409440 if self . options . verbose {
410441 eprintln ! ( "{command:?}" ) ;
411442 }
@@ -810,6 +841,15 @@ fn do_xargs(args: &[&str]) -> Result<CommandResult, XargsError> {
810841 invocation",
811842 ) ,
812843 )
844+ . arg (
845+ Arg :: with_name ( options:: REPLACE )
846+ . long ( options:: REPLACE )
847+ . short ( "R" )
848+ . takes_value ( true )
849+ . value_name ( "R" )
850+ . visible_aliases ( & [ "i" , "I" ] )
851+ . help ( "Replace R in INITIAL-ARGS with names read from standard input; if R is unspecified, assume {}" ) ,
852+ )
813853 . arg (
814854 Arg :: with_name ( options:: VERBOSE )
815855 . short ( "t" )
@@ -837,6 +877,16 @@ fn do_xargs(args: &[&str]) -> Result<CommandResult, XargsError> {
837877 size : matches
838878 . value_of ( options:: SIZE )
839879 . map ( |value| value. parse ( ) . unwrap ( ) ) ,
880+ replace : if matches. is_present ( options:: REPLACE ) {
881+ Some (
882+ matches
883+ . value_of ( options:: REPLACE )
884+ . unwrap_or ( "{}" )
885+ . to_string ( ) ,
886+ )
887+ } else {
888+ None
889+ } ,
840890 verbose : matches. is_present ( options:: VERBOSE ) ,
841891 } ;
842892
@@ -891,9 +941,10 @@ fn do_xargs(args: &[&str]) -> Result<CommandResult, XargsError> {
891941
892942 limiters. add ( MaxCharsCommandSizeLimiter :: new_system ( & env) ) ;
893943
894- let mut builder_options = CommandBuilderOptions :: new ( action, env, limiters) . map_err ( |_| {
895- "Base command and environment are too large to fit into one command execution"
896- } ) ?;
944+ let mut builder_options =
945+ CommandBuilderOptions :: new ( action, env, limiters, options. replace . clone ( ) ) . map_err (
946+ |_| "Base command and environment are too large to fit into one command execution" ,
947+ ) ?;
897948
898949 builder_options. verbose = options. verbose ;
899950 builder_options. close_stdin = options. arg_file . is_none ( ) ;
0 commit comments