有没有办法找到执行我在shell中定义的函数?

例如:

dosomething () {
  echo "Doing something with $1"
}
find . -exec dosomething {} \;

其结果是:

find: dosomething: No such file or directory

有没有办法让find's -exec看到做某事?


当前回答

我会完全避免使用-exec。使用xargs:

find . -name <script/command you're searching for> | xargs bash -c

其他回答

只是一个关于使用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

“find | while”和“for loop”似乎在速度上最好且相似。

为了对其他一些答案进行补充和澄清,如果您正在为exec或execdir (-exec命令{}+)使用bulk选项,并希望检索所有位置参数,则需要考虑使用bash -c处理$0。

更具体地说,考虑下面的命令,它使用上面建议的bash -c,并简单地从它找到的每个目录回显以'.wav'结尾的文件路径:

find "$1" -name '*.wav' -execdir bash -c 'echo "$@"' _ {} +

Bash手册说:

如果有-c选项,则从第一个非选项参数command_string中读取命令。如果在command_string之后有参数,则将它们分配给位置参数,从$0开始。

这里,'echo "$@"'是命令字符串,_{}是命令字符串之后的参数。注意,$@在Bash中是一个特殊的位置参数,它扩展到从1开始的所有位置参数。还要注意,使用-c选项时,第一个参数被分配给位置形参$0。

这意味着如果您尝试使用$@访问所有的位置参数,则只能获得$1及以上的参数。这就是为什么Dominik的答案有_,这是一个填充参数$0的哑参数,所以我们想要的所有参数都可以在以后使用,如果我们使用$@参数展开,或者在答案中使用for循环。

当然,类似于接受的答案,bash -c 'shell_function "$0" "$@"'也可以通过显式传递$0来工作,但同样,您必须记住$@不会像预期的那样工作。

把函数放在一个单独的文件中,然后get find来执行它。

Shell函数在定义它们的Shell内部;Find永远也看不到他们。

让脚本调用自身,将找到的每个项作为参数传递:

#!/bin/bash

if [ ! $1 == "" ] ; then
   echo "doing something with $1"
   exit 0
fi

find . -exec $0 {} \;

exit 0

当您单独运行脚本时,它会找到您正在寻找的内容,并调用自己,将每个查找结果作为参数传递。当脚本使用参数运行时,它会执行参数上的命令,然后退出。

批量处理结果

为了提高效率,许多人使用xargs批量处理结果,但这是非常危险的。因此,在find中引入了另一种方法来批量执行结果。

但是请注意,这种方法可能会带来一些警告,例如POSIX-find中要求在命令末尾使用{}。

export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +

Find会将许多结果作为参数传递给一次bash调用,for循环遍历这些参数,执行函数对每个参数执行一些操作。

上面的解决方案从$1开始参数,这就是为什么有_(代表$0)。

逐一处理结果

同样地,我认为公认的最上面的答案应该更正为

export -f dosomething
find . -exec bash -c 'dosomething "$1"' _ {} \;

这不仅更明智,因为参数应该总是以$1开始,而且如果find返回的文件名对shell具有特殊含义,则使用$0可能会导致意外的行为。