18

example use of xargs application in Unix can be something like this:

ls | xargs echo

which is the same as (let's say I have someFile and someDir/ in the working directory):

echo someFile someDir

so xargs take its input and place it at the end of the next command (here at the end of echo).

But sometimes I want xargs to place its input somewhere in the middle of next command.

For example:

find . -type f -name "*.cpp" -print | xargs g++ -o outputFile

so if I had in the current directory files a.cpp, b.cpp, c.cpp the output would be the same as with the command:

g++ -o outputFile a.cpp b.cpp c.cpp

but I want to have something like this:

g++ a.cpp b.cpp c.cpp -o outputFile

Is there a way to do it?

P.S.: I need it in some cases, because e.g.:

i586-mingw32msvc-g++ -o outputFile `pkg-config --cflags --libs gtkmm-2.4` a.cpp b.cpp c.cpp

doesn't work but this one works fine:

i586-mingw32msvc-g++ a.cpp b.cpp c.cpp -o outputFile `pkg-config --cflags --libs gtkmm-2.4`
Daniel Böhmer
  • 14,463
  • 5
  • 36
  • 46

4 Answers4

17

To answer the original question asked in the title of how to use xargs with the input in the middle rather than the end:

$ echo a b c | xargs -I {} echo before {} after
before a b c after

This replaces {} in the command with the piped output. There are some subtle differences between BSD and GNU xargs described below:

BSD xargs (e.g. on MacOS/Darwin, freebsd)

Use -I REPLACE, which will replace the string REPLACE (or whatever you pass) in the command. For example:

$ echo a b c | xargs -I {} echo before {} after
before a b c after
$ echo a b c | xargs -I REPLACE echo before REPLACE after
before a b c after
$ echo 'a
> b
> c' | xargs -L1 -I {} echo before {} after
before a after
before b after
before c after

The man page describes the option:

 -I replstr
     Execute utility for each input line, replacing one or more occur-
     rences of replstr in up to replacements (or 5 if no -R flag is
     specified) arguments to utility with the entire line of input.
     The resulting arguments, after replacement is done, will not be
     allowed to grow beyond replsize (or 255 if no -S flag is speci-
     fied) bytes; this is implemented by concatenating as much of the
     argument containing replstr as possible, to the constructed argu-
     ments to utility, up to replsize bytes.  The size limit does not
     apply to arguments to utility which do not contain replstr, and
     furthermore, no replacement will be done on utility itself.
     Implies -x.

GNU xargs (e.g. on Linux)

$ echo a b c | xargs -i echo before {} after
before a b c after
$ echo a b c | xargs -I THING echo before THING after
before a b c after

Use either the -I REPLACE or the the -i argument, which the man page describes:

   -I replace-str
          Replace occurrences of replace-str in the initial-arguments
          with names read from standard input.  Also, unquoted blanks do
          not terminate input items; instead the separator is the
          newline character.  Implies -x and -L 1.

   -i[replace-str], --replace[=replace-str]
          This option is a synonym for -Ireplace-str if replace-str is
          specified.  If the replace-str argument is missing, the effect
          is the same as -I{}.  This option is deprecated; use -I
          instead.

The -L 1 on -I means that it will execute each of the input in a separate command:

$ echo "a
> b
> c" | xargs -I THING echo before THING after
before a after
before b after
before c after
Lucas Wiman
  • 10,021
  • 2
  • 37
  • 41
  • 1
    Regarding the last sentence in parentheses: `xargs -i` has exactly the same effect `xargs -I` w.r.t. line breaks. The only difference between `-i` and `-I` is, that `-i` defaults to `{}` for the replacement string, while `-I` does not have any default. – Elmar Zander Aug 29 '23 at 12:46
  • 1
    @ElmarZander Indeed, thank you! Not sure what I was thinking 6 years ago, but I wasn't able to produce a difference, even on a very old docker image from that vintage. I removed that sentence. – Lucas Wiman Aug 29 '23 at 19:18
6

If your version of xargs doesn't include the -I feature, an alternative is to write a little shell script containing the command you want to execute:

#!/bin/sh
exec i586-mingw32msvc-g++ "$@" -o outputFile...

Then use xargs to run that:

find . -type f -name "*.cpp" -print | xargs my_gcc_script
Daniel Böhmer
  • 14,463
  • 5
  • 36
  • 46
Kenster
  • 23,465
  • 21
  • 80
  • 106
1

You do not need xargs for this. Just use:

g++ `find . -type f -name '*.cpp'` -o outputFile
mark4o
  • 58,919
  • 18
  • 87
  • 102
  • 2
    I'd say use $() over back ticks, but it works so I can't complain. – scragar Sep 05 '09 at 17:12
  • I used backtick because he didn't say what shell he was using, and backtick works with more of them (e.g. tcsh). – mark4o Sep 05 '09 at 17:21
  • 2
    You really shouldn't do this. Spaces will break this. – Draemon Jan 16 '12 at 01:26
  • This also doesn't work in the scenario (which is one of the things `xargs` is made for!) when you have more arguments than can fit on a single command line (`getconf ARG_MAX` for the total combined size of all environment variables and command-line arguments permitted for a single process). Granted, `g++` will misbehave in that case either way, but Q&A entries get used beyond the scope of the single person who first asked the question. – Charles Duffy Oct 23 '19 at 20:23
1

GNU Parallel http://www.gnu.org/software/parallel/ would be a solution too:

find . -type f -name "*.cpp" -print | parallel -Xj1 g++ {} -o outputFile

The *.cpp must fit in a single line (~128 KB).

Ole Tange
  • 31,768
  • 5
  • 86
  • 104
  • If I use `-X` without `-j1`, it gets split over multiple commands, one command per item piped in. With `-j1` (one process) it works for me. I'd also suggest `find -print0` and `parallel -0`. – Joshua Goldberg Dec 17 '21 at 18:46