Get argument from pipe

Dávid Natingga picture Dávid Natingga · Jul 25, 2013 · Viewed 23.3k times · Source

Consider having the results from the pipe:

find .

Now I would like to access in the second command behind the pipe what is actually piped (inputed) and then for example to print it twice.

find . | printf $arg$arg\n
#each filename would be printed twice per line

Please note that the question is not asking about printing whatever once gets from pipe twice, I know how to use bash for loop or write a script that could accomplish the mentioned. How I can get $arg to use it quickly in inline scripts?

$0 and $1 do not work as they do in scripting files.

Answer

Jonathan Leffler picture Jonathan Leffler · Jul 25, 2013

Ignoring the possibility that file names contain newlines, you can use sed as pringley suggests in his answer, or you can create a while loop with the read command:

find . |
while read -r line
do
    echo "$line$line"
done

(The -r is for 'raw' input; it stops the shell expanding backslash escape sequences in the input, and is a standard POSIX feature of the read command in the shell.)

Since you mention bash, you can avoid problems with newlines in the file names by using the find . -print0 option to terminate each name by a null byte ('\0' in C) instead of a newline, and then use:

find . -print0 |
while read -r -d '' line
do
    echo "X${line}${line}X"
done

The -d '' replaces the normal newline delimiter with the first character of the string argument, but the string is empty so the first character is the only character is a null byte.

There isn't an easy way (nor, as far as I know, a hard way) to use a for loop along the lines of:

for line in $(find .)
do
    echo "X${line}${line}X"
done

which works reliably for names with spaces, or newlines in them.

Often, you may want to use the xargs command. This reads standard input and creates a command line using what's read from standard input as arguments to the command.

find . | xargs wc -l

Or, with newline and space safety (by default, xargs splits arguments at spaces, tabs and newlines):

find . -type f -print0 | xargs -0 wc -l

There are a lot of options you can apply to xargs — especially the GNU version.

However, you also have options to find that largely make xargs redundant:

find . -type f -exec wc -l {} +

which does basically the same job as the find . -print0 | xargs -0 wc -l command. One difference is that if there are no files in the output from find, then using -exec won't execute wc at all, whereas by default, xargs will execute it once (with no file name argument; this is for POSIX compliance). With GNU xargs, you can use -r or --no-run-if-empty to stop that happening. Mac OS X xargs seems to do that anyway.