有没有办法让find
执行我在shell中定义的函数?例如:
dosomething () {
echo "doing something with $1"
}
find . -exec dosomething {} \;
结果是:
find: dosomething: No such file or directory
有没有办法让find
的{{1}}看到-exec
?
答案 0 :(得分:226)
由于只有shell知道如何运行shell函数,因此必须运行shell来运行函数。您还需要使用export -f
标记要导出的函数,否则子shell将不会继承它们:
export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;
答案 1 :(得分:95)
find . | while read file; do dosomething "$file"; done
答案 2 :(得分:18)
在{}
中添加引号,如下所示:
export -f dosomething
find . -exec bash -c 'dosomething "{}"' \;
这可以纠正由find
返回的特殊字符引起的任何错误,
例如,名称中带有括号的文件。
答案 3 :(得分:17)
Jak上面的回答非常好,但有一些容易克服的陷阱:
find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done
这使用null作为分隔符而不是换行符,因此带有换行符的文件名将起作用。它还使用-r
标志来禁用反斜杠转义,如果文件名中的反斜杠不起作用。它还清除IFS
,以便不丢弃名称中潜在的尾随空格。
答案 4 :(得分:8)
让脚本调用自身,传递作为参数找到的每个项目:
#!/bin/bash
if [ ! $1 == "" ] ; then
echo "doing something with $1"
exit 0
fi
find . -exec $0 {} \;
exit 0
当您自己运行脚本时,它会找到您要查找的内容并调用自身将每个查找结果作为参数传递。当脚本使用参数运行时,它会对参数执行命令,然后退出。
答案 5 :(得分:7)
为了提高效率,许多人使用xargs
批量处理结果,但这非常危险。因此,find
中引入了一种替代方法,可以批量执行结果。
请注意,此方法可能会附带一些警告,例如POSIX中的要求 - find
在命令末尾有{}
。
export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +
find
会将许多结果作为参数传递给bash
的单个调用,并且for
- 循环遍历这些参数,在每个参数上执行函数dosomething
那些。
以上解决方案在$1
处启动参数,这就是_
(代表$0
)的原因。
同样地,我认为应该将接受的最佳答案更正为
export -f dosomething
find . -exec bash -c 'dosomething "$1"' _ {} \;
这不仅更加明智,因为参数应始终从$1
开始,但如果$0
返回的文件名具有特殊含义,则使用find
可能会导致意外行为外壳
答案 6 :(得分:2)
对于那些寻找将在当前目录中的所有文件上执行给定命令的bash函数的人,我已经从上面的答案中编译了一个:
toall(){
find . -type f | while read file; do "$1" "$file"; done
}
请注意,它会打破包含空格的文件名(见下文)。
例如,使用此功能:
world(){
sed -i 's_hello_world_g' "$1"
}
假设我想在当前目录的所有文件中将hello的所有实例更改为world。我愿意:
toall world
为了安全使用文件名中的任何符号,请使用:
toall(){
find . -type f -print0 | while IFS= read -r -d '' file; do "$1" "$file"; done
}
(但您需要find
来处理-print0
,例如GNU find
)。
答案 7 :(得分:2)
如果您对exec
或execdir
(-exec command {} +
)使用批量选项,并且想要检索所有位置参数,则可以为其他一些答案提供补充和说明。 ,则需要考虑使用$0
处理bash -c
。更具体地说,考虑下面的命令,该命令如上所述使用bash -c
,并从找到的每个目录中简单地回显以'.wav'结尾的文件路径:
find "$1" -name '*.wav' -execdir bash -c 'echo $@' _ {} +
bash手册说:
If the -c option is present, then commands are read from the first non-option argument command_string. If there are arguments after the command_string, they are assigned to the
positional parameters, starting with $0.
在这里,'check $@'
是命令字符串,_ {}
是命令字符串后面的参数。请注意,$@
是bash中的特殊位置参数,它会扩展为所有从<1>开始的位置参数。还要注意,使用-c
选项,第一个参数被分配给位置参数$0
。这意味着,如果您尝试使用$@
访问所有位置参数,则只会获得从$1
开始的参数。这就是为什么Dominik的答案使用_
(它是填充参数$0
的伪参数)的原因,因此,如果我们使用$@
参数扩展来填充,我们想要的所有参数将在以后可用。实例或该答案中的for循环。
当然,与接受的答案类似,bash -c 'shell_function $0 $@'
也可以通过显式传递$0
来工作,但是同样,您必须记住$@
不能像预期的。
答案 8 :(得分:1)
无法以这种方式执行功能。
要解决此问题,您可以将函数放在shell脚本中并从find
# dosomething.sh
dosomething () {
echo "doing something with $1"
}
dosomething $1
现在在find as中使用它:
find . -exec dosomething.sh {} \;
答案 9 :(得分:1)
将该函数放在一个单独的文件中,然后让find
执行该操作。
Shell函数是它们定义的shell的内部函数; find
永远无法看到它们。
答案 10 :(得分:1)
我发现最简单的方法如下,在单一操作中重复两个命令
func_one () {
echo "first thing with $1"
}
func_two () {
echo "second thing with $1"
}
find . -type f | while read file; do func_one $file; func_two $file; done
答案 11 :(得分:1)
作为参考,我避免使用以下情况:
for i in $(find $dir -type f -name "$name" -exec ls {} \;); do
_script_function_call $i;
done;
获取当前脚本文件中find的输出,并根据需要迭代输出。 我确实同意接受的答案,但是我不想在脚本文件之外公开函数。
答案 12 :(得分:1)
供您参考, 这是 bash 下各种解决方案的基准, 包括一个简单的 for 循环案例: (1465 个目录,在标准硬盘上,armv7l GNU/Linux synology_armada38x_ds218j)
dosomething() { 回声 $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
“find | while”和“for 循环”在速度上似乎最好且相似。
答案 13 :(得分:0)
不直接,不。查找是在单独的进程中执行,而不是在shell中执行。
创建一个与您的函数执行相同工作的shell脚本,并找到-exec
那个。
答案 14 :(得分:-2)
我会完全避免使用-exec
。为什么不使用xargs
?
find . -name <script/command you're searching for> | xargs bash -c