在Linux中查找-exec shell函数?

时间:2010-12-01 05:25:35

标签: linux bash shell find bsd

有没有办法让find执行我在shell中定义的函数?例如:

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

结果是:

find: dosomething: No such file or directory

有没有办法让find的{​​{1}}看到-exec

15 个答案:

答案 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)

如果您对execexecdir-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