This is a quick tribute to and summary of
xargs, the glue that holds together most of my shell scripts.
Imagine you have a file called
$ cat fruits.txt apples.txt oranges.txt bananas.txt
It’s a file with fruits all listed out on separate lines.
xargs lets you squash that list down into a single line:
$ cat fruits.txt | xargs apples.txt oranges.txt bananas.txt
What it’s doing is taking a list of things and turning them into arguments.
What do I mean by “arguments”? Let’s look at this example…
$ rm apples.txt oranges.txt bananas.txt
In that statement, we’re passing three arguments to the
rm command, which will remove the files.
In order to pass multiple file names to
rm, they need to be written as arguments, meaning they’re all on one line and separated by spaces.
So… what if we have a
fruits.txt that contains a list of filenames, and we want to execute the
rm program, and pass it that list of files as arguments?
We can do it like this:
$ cat fruits.txt | xargs rm
In English, you might read this as “Take the contents of
fruits.txt, turn that into arguments, and then execute the
rm command with those arguments”.
This kind of problem comes up all the time when writing shell scripts. For me, it comes up most often when I’m writing one liners that I’m executing at the command line. Here are a few real examples I found in my shell history:
$ git ls-files | grep -E "\.(rb|jbuilder|ru)$" | xargs rubyfmt --write
In other words: “Find all of the ruby files in my git repository and format them with rubyfmt.”
$ git ls-files | grep "test.jsx" | xargs grep -l enzyme | xargs rm
(This one has a double
In other words: “Find all of the react tests in my git repository and then search just those files for the word enzyme, and then print out the list of those file names, and then pass them as arguments to the
That last example is a pretty good category of problem that
I often search through a codebase to find source code that matches a pattern.
It’s easy enough to do that with a simple
grep (or the search in your preferred editor).
But I occasionally want to narrow down the search to a subset of files, and that’s not always easy to do with a single statement.
For example, I might want to search for “Find me everywhere in this codebase that contains the string
FOO but narrowed down to files that happen to also contain the string
That’s easy enough to achieve once you’re comfortable with
$ grep -lr BAR . | xargs grep FOO ./app/models/human.rb: puts FOO.inspect
Note: in my day-to-day life, I’m generally using ripgrep, not grep, but I wanted to keep the examples simple by using more standard things. But once you’re using ripgrep, the command gets a bit simpler and faster too:
$ rg -l BAR | xargs rg FOO app/models/human.rb 22: puts FOO.inspect
All right, that’s all I got. Have fun.