Alternatives to xargs -l

eatloaf picture eatloaf · Jan 6, 2012 · Viewed 9.4k times · Source

I want to rename a bunch of dirs from DIR to DIR.OLD. Ideally I would use the following:

find . -maxdepth 1 -type d -name \"*.y\" -mtime +`expr 2 \* 365` -print0 | xargs -0 -r -I file mv file file.old

But the machine I want to execute this on has BusyBox installed and the BusyBox xargs doesn't support the "-I" option.

What are some common alternative methods for collecting an array of files and then executing on them in a shell script?

Answer

praetorian droid picture praetorian droid · Jan 6, 2012

You can use -exec and {} features of the find command so you don't need any pipes at all:

find -maxdepth 1 -type d -name "*.y" -mtime +`expr 2 \* 365` -exec mv "{}" "{}.old" \;

Also you don't need to specify '.' path - this is default for find. And you used extra slashes in "*.y". Of course if your file names do not really contain quotes.

In fairness it should be noted, that version with while read loop is the fastest of proposed here. Here are some example measurements:

$ cat measure 
#!/bin/sh
case $2 in
  1) find "$1" -print0 | xargs -0 -I file echo mv file file.old ;;

  2) find "$1" -exec echo mv '{}' '{}.old' \; ;;

  3) find "$1" | while read file; do
       echo mv "$file" "$file.old"
     done;;
esac
$ time ./measure android-ndk-r5c 1 | wc
   6225   18675  955493
real    0m6.585s
user    0m18.933s
sys     0m4.476s
$ time ./measure android-ndk-r5c 2 | wc
   6225   18675  955493
real    0m6.877s
user    0m18.517s
sys     0m4.788s
$ time ./measure android-ndk-r5c 3 | wc
   6225   18675  955493
real    0m0.262s
user    0m0.088s
sys     0m0.236s

I think it's because find and xargs invokes additional /bin/sh (actually exec(3) does it) every time for execute a command, while shell while loop do not.

Upd: If your busybox version was compiled without -exec option support for the find command then the while loop or xargs, suggested in the other answers (one, two), is your way.