Search code examples
linuxbashshellfindxargs

xargs sh -c skipping the first argument


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;'
b
d
f
$ 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.


Solution

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

    Examples:

    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