问题
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?
回答1:
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.
回答2:
Use a
forloop. Unfortunately I don't think busybox understandsread -0either, so you won't be able to handle newlines properly. If you don't need to, it's easiest to just:find . -maxdepth 1 -type d -name \"*.y\" -mtime +`expr 2 \* 365` -print | while read file; do mv -- "$file" "$file".old; doneUse a
sh -cas the command. Note the slightly weird use of$0to name the first argument (it would normally be the script name and that goes to$0and while you are suppressing script with-c, the argument still goes to$0) and the use of-n 1to avoid batching.find . -maxdepth 1 -type d -name \"*.y\" -mtime +`expr 2 \* 365` -print0 | xargs -0 -r -n 1 sh -c 'mv -- "$0" "$0".old'
Edit Oops: I forgot about the find -exec again.
回答3:
An alternative is to use a loop:
find . -maxdepth 1 -type d -name \"*.y\" -mtime +`expr 2 \* 365` -print | while IFS= read file
do
mv "$file" "$file".old
done
来源:https://stackoverflow.com/questions/8759285/alternatives-to-xargs-l