Alternatives to xargs -l
Solution 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.
Solution 2
-
Use a
for
loop. Unfortunately I don't think busybox understandsread -0
either, 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; done
-
Use a
sh -c
as the command. Note the slightly weird use of$0
to name the first argument (it would normally be the script name and that goes to$0
and while you are suppressing script with-c
, the argument still goes to$0
) and the use of-n 1
to 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.
Solution 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
eatloaf
Updated on June 14, 2022Comments
-
eatloaf almost 2 years
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?
-
Jan Hudec over 12 years
read
uses newline as separator. Either you needread -0
(but I am not sure it's implemented in busybox) or you need just-print
. -
David W. over 12 years+1: The find command comes with the
-exec
option. It's a bit slower thanxargs
because it executes the command for each and every file found. But, it works, and it doesn't have whitespace issues because the shell isn't involved. If you can't usexargs
because of whitespace issues, use-exec
. Loops are just as inefficient as the-exec
parameter since they execute once for each and every file, but have the whitespace issues thatxargs
have. -
praetorian droid over 12 years
xargs
also executes the command for each file in this case sofind
is not slower. -
David W. over 12 yearsYou're right. I didn't look at the command. Normally you use xargs instead of
-exec
because xargs combines as many files as it can on the command line and executes the command only a few times. -
Hello71 over 9 yearswrong; it is because find and exec do not run shells so must exec /bin/echo for each file. if we modify the script to use /bin/echo then the time is comparable to the first two options; adding an option
find "$1" -printf "mv %p %p.old\n"
runs faster than the builtin echo. -
Greg Rundlett over 8 yearsBusybox doesn't even understand print0. But your whie loop works
-
Greg Rundlett over 8 yearsThis doesn't work as written, because Busybox doesn't support the -exec option to find. The while loop approach posted by Jan Hudec works on Busybox
-
Charles Duffy over 8 years@DavidW., re: "slower",
-exec ... {} +
behaves just as xargs does (in terms of minimizing the invocation count), and has been part of the POSIX standard forfind
since 2006. -
praetorian droid over 8 yearsIn busybox '-exec' option support is configurable at compile time just like the 'find' command itself. Updated the answer.