Piping and xargs (updated)
Today I faced an odd problem. I needed to remove everything but a specific file from a directory via the command line. Having thought about it for a while, and done a bit of research, I came across someone posing the same question on Stack exchange. There are several answers provided, but I really liked this one.
Example
ls | grep -v file.txt | xargs -t rm
In this example, everything but file.txt will be removed from the working directory.
How it works
- The output from the
lscommand is piped intogrep grepis passed the-vargument which inverts its behaviour, returning everything butfile.txt- The output of
grepis then piped intoxargswhich in turn passes each result torm(which removes each item passed to it). Note: the-targument simply shows the resulting command that is run byxargs
I thought that was a nice simple way of achieving what I needed and, since it also neatly demonstrates how piping and xargs works, I thought I’d post it here.
Using xargs with commands that take two arguments
But what about using xargs for commands that take multiple arguments (like mv that takes both source and destination arguments)? I used this command today to move all Python files within the current directory to a new location.
ls | grep -e '.*py$' | xargs -I '{}' mv '{}' ../python
How it works
- The output of
lsis piped togrep - the
-eargument is passed togrepto use extended regular expressions - the output is passed to
xargswith the-Iargument using'{}'to represent the replacement.
I should explain that I initially found this confusing because it looked to me that standard input was being placed both before and after the mv command. That isn’t the case because the first instance of '{}' is simply providing a name for the value received from standard input. I’ve experimented with this and you could achieve the exact same thing with something other than '{}'. Here’s an example where I’m copying files using '{blah}':
ls | grep .py | xargs -I {blah} cp {blah} new-{blah}