alx*_*ndr 169 linux bash shell bsd find
有没有办法find
执行我在shell中定义的函数?例如:
dosomething () {
echo "doing something with $1"
}
find . -exec dosomething {} \;
Run Code Online (Sandbox Code Playgroud)
结果是:
find: dosomething: No such file or directory
Run Code Online (Sandbox Code Playgroud)
有没有办法让find
的-exec
看dosomething
?
Ada*_*eld 241
由于只有shell知道如何运行shell函数,因此必须运行shell来运行函数.您还需要标记要导出的函数export -f
,否则子shell将不会继承它们:
export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;
Run Code Online (Sandbox Code Playgroud)
小智 107
find . | while read file; do dosomething "$file"; done
Run Code Online (Sandbox Code Playgroud)
小智 18
添加引号{}
如下所示:
export -f dosomething
find . -exec bash -c 'dosomething "{}"' \;
Run Code Online (Sandbox Code Playgroud)
这可以纠正由于返回的特殊字符而导致的任何错误find
,例如名称中带有括号的文件.
小智 18
Jak的答案很好,但有一些容易克服的陷阱:
find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done
Run Code Online (Sandbox Code Playgroud)
这使用null作为分隔符而不是换行符,因此带有换行符的文件名将起作用.它还使用-r
禁用反斜杠转义的标志,没有文件名中的反斜杠将无法正常工作.它也会清除,IFS
以便不丢弃名称中潜在的尾随空格.
小智 8
让脚本调用自己,传递作为参数找到的每个项目:
#!/bin/bash
if [ ! $1 == "" ] ; then
echo "doing something with $1"
exit 0
fi
find . -exec $0 {} \;
exit 0
Run Code Online (Sandbox Code Playgroud)
当您单独运行脚本时,它会找到您要查找的内容并调用自身将每个查找结果作为参数传递.当脚本使用参数运行时,它会对参数执行命令,然后退出.
为了提高效率,许多人使用xargs
大量处理结果,但这是非常危险的.因此,引入了另一种方法find
来批量执行结果.
不过请注意,此方法可能附带像例如在POSIX-要求一些注意事项find
有{}
在命令结束.
export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +
Run Code Online (Sandbox Code Playgroud)
find
将多个结果作为参数传递给单个调用,bash
并且for
-loop遍历这些参数,dosomething
在每个参数上执行函数.
上面的解决方案开始论证$1
,这就是为什么有一个_
(代表$0
).
以同样的方式,我认为应该纠正被接受的最佳答案
export -f dosomething
find . -exec bash -c 'dosomething "$1"' _ {} \;
Run Code Online (Sandbox Code Playgroud)
这不仅更加理智,因为参数应该始终开始$1
,但$0
如果返回的文件名find
对shell具有特殊含义,则使用可能会导致意外行为.
只是关于使用 shell 的已接受答案的警告,尽管它很好地回答了问题,但它可能不是在查找结果上执行某些代码的最有效方法:
这是 bash 下各种解决方案的基准测试,包括一个简单的 for 循环情况:(1465 个目录,在标准硬盘驱动器上,armv7l GNU/Linux synology_armada38x_ds218j)
dosomething() { echo $1; }
export -f dosomething
time find . -type d -exec bash -c 'dosomething "$0"' {} \;
real 0m16.102s
time while read -d '' filename; do dosomething "${filename}" </dev/null; done < <(find . -type d -print0)
real 0m0.364s
time find . -type d | while read file; do dosomething "$file"; done
real 0m0.340s
time for dir in $(find . -type d); do dosomething $dir; done
real 0m0.337s
Run Code Online (Sandbox Code Playgroud)
“find | while”和“for循环”似乎最好并且速度相似。
对于那些正在寻找将对当前目录中的所有文件执行给定命令的 Bash 函数的人,我从上述答案中编译了一个:
toall(){
find . -type f | while read file; do "$1" "$file"; done
}
Run Code Online (Sandbox Code Playgroud)
请注意,它打破了包含空格的文件名(见下文)。
以这个函数为例:
world(){
sed -i 's_hello_world_g' "$1"
}
Run Code Online (Sandbox Code Playgroud)
假设我想将当前目录中所有文件中“hello”的所有实例更改为“world”。我会做:
toall world
Run Code Online (Sandbox Code Playgroud)
为了安全使用文件名中的任何符号,请使用:
toall(){
find . -type f -print0 | while IFS= read -r -d '' file; do "$1" "$file"; done
}
Run Code Online (Sandbox Code Playgroud)
(但您需要find
处理-print0
例如 GNU find
)。