xargs -l的替代方案

8

我想将一些目录从DIR重命名为DIR.OLD。理想情况下,我希望使用以下命令:

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

但是我想在的机器上安装了BusyBox,并且BusyBox xargs不支持“-I”选项。

在shell脚本中收集文件数组并对其执行的一些常见替代方法是什么?


2
“"”是什么意思?这将告诉find列出名为"*.y"的文件(引号将被传递给find,而*将由shell扩展,但它将找不到任何内容并继续传递),我怀疑这样的文件不存在。 - Jan Hudec
在busybox的各种编译时选项中,包括CONFIG_FEATURE_FIND_PRINT0CONFIG_FEATURE_FIND_EXECCONFIG_FEATURE_FIND_EXEC_PLUS -- 这些方法是否可行取决于您的副本编译的具体细节。 - Charles Duffy
3个回答

12
你可以使用find命令的-exec{}功能,这样就不需要使用管道了:
find -maxdepth 1 -type d -name "*.y" -mtime +`expr 2 \* 365` -exec mv "{}" "{}.old" \;

此外,您不需要指定“。”路径-这是find的默认路径。并且您在"*.y"中使用了额外的斜杠。当然,如果您的文件名实际上不包含引号。
公平地说,应该注意到,使用while read循环的版本是提出的版本中最快的。以下是一些示例测量:
$ 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

我认为这是因为findxargs每次执行命令时都会调用其他的/bin/sh (实际上是exec(3)),而shell的while循环则不会。

更新:如果你的busybox版本编译时没有支持-exec选项的find命令,则另外两个答案(onetwo)中建议的while循环或xargs就是你的选择。


1
xargs 在这种情况下也会为每个文件执行命令,因此 find 不会变慢。 - praetorian droid
错误;这是因为find和exec不运行shell,所以必须为每个文件执行/bin/echo。如果我们修改脚本使用/bin/echo,那么时间与前两个选项相当;添加一个选项“find“$1”-printf“mv%p%p.old\n””比内置的echo运行更快。 - Hello71
这段代码无法正常工作,因为Busybox不支持find命令的-exec选项。Jan Hudec发布的while循环方法可以在Busybox上运行。 - Greg Rundlett
@DavidW. 关于“慢”的问题,-exec ... {} + 的行为与 xargs 相同(在最小化调用次数方面),并且自 2006 年以来一直是 find POSIX 标准的一部分。 - Charles Duffy
在 Busybox 中,'-exec' 选项的支持与 'find' 命令本身一样,在编译时是可配置的。已更新答案。 - praetorian droid
显示剩余2条评论

3
  1. Use a for loop. Unfortunately I don't think busybox understands read -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
    
  2. 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'
    

编辑 糟糕:我又忘记了关于find -exec的事情。


Busybox甚至不理解print0。但是你的while循环有效。 - Greg Rundlett

1

另一种方法是使用循环:

find . -maxdepth 1 -type d -name \"*.y\" -mtime +`expr 2 \* 365` -print | while IFS= read file
do
    mv "$file" "$file".old
done

1
read 使用换行符作为分隔符。你需要使用 read -0(但我不确定它是否在 busybox 中实现)或者只需要使用 -print - Jan Hudec

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接