• Uncategorized

About linux : xargs-sh–c-skipping-the-first-argument

Question Detail

I’m trying to write a script that uses find and xargs to archive old files in a large directory. Here is the line:

find /tmp/messages/ -mtime +9 -print0 | xargs -x -t -0 -n 1000 sh -c ‘tar rPf /tmp/backup.tar “$@” && rm -f “$@”; sleep 1 && echo Finished one loop: $(date +”%T”)’

The script mostly works but it skips the first file each time the commands run for 1000 files and I can’t seem to figure out why.

Here is another example (I tried playing around with simpler commands to see what happens:

If I just use echo and xargs, I can run one command effectively:

$ echo a b c d e f| xargs -n 2 echo “$@”
a b
c d
e f
$ echo a b c d e f| xargs -n 3 echo “$@”
a b c
d e f

On the other hand, if I want to sleep every time I print each set, I add sh -c to add a string of commands:

$ echo a b c d e f| xargs -n 2 sh -c ‘echo “$@”; sleep 1;’
$ echo a b c d e f| xargs -n 3 sh -c ‘echo “$@”; sleep 1;’
b c
e f

See how the first item is skipped?

Any ideas on what could be going on here?

Thanks in advance! And let me know if you need any extra info, I’d be glad to provide it.

Question Answer

First argument to sh -c or bash -c is the name of the script i.e. $0 which is not printed when you use $@:


echo a b c d e f| xargs -n 3 bash -c ‘echo “$0 $@”‘
a b c
d e f

To fix this you can pass _ as dummy name of the script and then it should work:

echo a b c d e f| xargs -n 3 bash -c ‘echo “$@”‘ _
a b c
d e f

It will work fine even with your sleep example:

echo a b c d e f| xargs -n 3 bash -c ‘echo “$@”; sleep 1’ _
a b c
d e f

In the Shell Command language the @ special parameter

Expands to the positional parameters, starting from one.

The sh command is invoked in the following form:

sh -c[OPTIONS] command_string [command_name [argument…]]

From the documentation for the -c option:

Read commands from the command_string operand. Set the value of special parameter 0 (see Special Parameters) from the value of the command_name operand and the positional parameters ($1, $2, and so on) in sequence from the remaining argument operands.

For example, in the following command:

sh -c ‘tar czvf arch.tar.gz $@’ a b c

command_string is ‘tar czvf arch.tar.gz $@’,
command_name is a,
the first argument is b
the second argument is c

Thus, within the command string $0 will expand to a, $1 to b, $2 to c, and the $@ special parameter will expand to b c.

So if you want to use the command_name within the command string, refer to the special parameter zero, i.e. $0.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.