M y    b r a i n    h u r t s  !                                           w e                 r e a l l y                 t h i n k                   w h a t                y o u             k n o w

25 June 2011

Bash: Run command on each line in a file

This script runs a user-defined command on each line in a text file. The filename should come as the first argument, everything following it will be interpreted as the command name with arguments, if any.

For example, if you have a list of file names saved to a file, and want to do something with them, e.g. see file details, you could run `foreach filelist.txt ls -alh`.

Long Version

#!/bin/bash

if [ "$#" == "0" ]; then
    echo -e "USAGE:\n    `basename $0` <filename.txt> <cmd args...>"
    exit 1
fi

list=$1

shift 1

args="$*"

while read -r line <&3
do
 echo $args $line
 eval $args $line 
done 3<"$list"

Shorter Version — the power of xargs

Things are much simplier with xargs - it will read a list of arguments from standard input and run a command on each argument. By default arguments are separated by space, but you can specify your own separator with the -d (delimiter) option.

If you work with file names you might want to use xargs -0 to specify that the parameters are delimited by the the null separator. This is useful for example when you need to correctly handle file names containing spaces or other wierd characters. To generate the parameters with the null separators you could use find -print0 to find the files matching your criteria.

Here is an example that finds all .avi files in the current folder and print detailed info using `file` utility.

find . -iname "*.avi" -print0 | xargs -0 file

But find by itself is pretty powerful and can handle this particular use case on its own without using xargs.

find . -iname "*.avi" -exec file "{}" \;
Note that there is an important difference between the two commands: the xargs-version will only run file once passing all the parameters in one go, while find -exec will run the file command multiple times for each argument, so if the program you are running takes a while to start the performance in the second case might be significantly worse.

The Java Posse (Most Recent Podcasts)