'查找-exec' Linux 中的 shell 函数

发布于 2024-10-05 12:30:03 字数 328 浏览 10 评论 0原文

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

例如:

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

结果是:

find: dosomething: No such file or directory

有没有办法让find-exec看到dosomething

Is there a way to get find to execute a function I define in the shell?

For example:

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

The result of that is:

find: dosomething: No such file or directory

Is there a way to get find's -exec to see dosomething?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

旧话新听 2024-10-12 12:30:03

由于只有 shell 知道如何运行 shell 函数,因此您必须运行 shell 才能运行函数。您还需要使用 export -f 将函数标记为导出,否则子 shell 将不会继承它们:

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

Since only the shell knows how to run shell functions, you have to run a shell to run a function. You also need to mark your function for export with export -f, otherwise the subshell won't inherit them:

export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;
冬天旳寂寞 2024-10-12 12:30:03
find . | while read file; do dosomething "$file"; done
find . | while read file; do dosomething "$file"; done
此生挚爱伱 2024-10-12 12:30:03

Jac的答案很棒,但有一些问题容易克服的陷阱:

find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done

这使用 null 作为分隔符而不是换行符,因此带有换行符的文件名将起作用。它还使用 -r 标志来禁用反斜杠转义,如果没有它,文件名中的反斜杠将不起作用。它还会清除IFS,以便名称中潜在的尾随空格不会被丢弃。

Jac's answer is great, but it has a couple of pitfalls that are easily overcome:

find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done

This uses null as a delimiter instead of a linefeed, so filenames with line feeds will work. It also uses the -r flag which disables backslash escaping, and without it backslashes in filenames won't work. It also clears IFS so that potential trailing white spaces in names are not discarded.

萌逼全场 2024-10-12 12:30:03

{} 中添加引号,如下所示:

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

这可以纠正由于 find 返回特殊字符而导致的任何错误,
例如名称中带有括号的文件。

Add quotes in {} as shown below:

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

This corrects any error due to special characters returned by find,
for example files with parentheses in their name.

猥琐帝 2024-10-12 12:30:03

批量处理结果

为了提高效率,很多人使用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 可能会导致意外行为。

Processing results in bulk

For increased efficiency, many people use xargs to process results in bulk, but it is very dangerous. Because of that there was an alternate method introduced into find that executes results in bulk.

Note though that this method might come with some caveats like for example a requirement in POSIX-find to have {} at the end of the command.

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

find will pass many results as arguments to a single call of bash and the for-loop iterates through those arguments, executing the function dosomething on each one of those.

The above solution starts arguments at $1, which is why there is a _ (which represents $0).

Processing results one by one

In the same way, I think that the accepted top answer should be corrected to be

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

This is not only more sane, because arguments should always start at $1, but also using $0 could lead to unexpected behavior if the filename returned by find has special meaning to the shell.

心凉 2024-10-12 12:30:03

只是关于为每个查找结果启动 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 循环”似乎速度最好且相似。

Just a warning regaring the accepted answer that is starting a shell for each find results:

Despite it well answer the question, it might not be the most efficient way to exec some code on find results:

Here is a benchmark under bash of all kind of solutions,
including a simple for loop case:
(1465 directories, on a standard hard drive, 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" and "for loop" seems best and similar in speed.

温柔戏命师 2024-10-12 12:30:03

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

#!/bin/bash

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

find . -exec $0 {} \;

exit 0

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

Have the script call itself, passing each item found as an argument:

#!/bin/bash

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

find . -exec $0 {} \;

exit 0

When you run the script by itself, it finds what you are looking for and calls itself passing each find result as the argument. When the script is run with an argument, it executes the commands on the argument and then exits.

手心的温暖 2024-10-12 12:30:03

对于那些正在寻找对当前目录中的所有文件执行给定命令的 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
}

但您需要一个处理-print0find,例如GNU find )。

For those of you looking for a Bash function that will execute a given command on all files in current directory, I have compiled one from the above answers:

toall(){
    find . -type f | while read file; do "$1" "$file"; done
}

Note that it breaks with file names containing spaces (see below).

As an example, take this function:

world(){
    sed -i 's_hello_world_g' "$1"
}

Say I wanted to change all instances of "hello" to "world" in all files in the current directory. I would do:

toall world

To be safe with any symbols in filenames, use:

toall(){
    find . -type f -print0 | while IFS= read -r -d '' file; do "$1" "$file"; done
}

(but you need a find that handles -print0 e.g., GNU find).

何时共饮酒 2024-10-12 12:30:03

不可能以这种方式执行函数

为了克服这个问题,您可以将函数放在 shell 脚本中并从 find 中调用它,

# dosomething.sh
dosomething () {
  echo "doing something with $1"
}
dosomething $1

现在在 find 中使用它,如下所示:

find . -exec dosomething.sh {} \;

It is not possible to executable a function that way.

To overcome this you can place your function in a shell script and call that from find

# dosomething.sh
dosomething () {
  echo "doing something with $1"
}
dosomething $1

Now use it in find as:

find . -exec dosomething.sh {} \;
倦话 2024-10-12 12:30:03

如果您使用 execexecdir 的批量选项(-exec command {} +),并且想要检索所有位置参数,您需要考虑使用 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 来工作,但同样,您将有请记住,$@ 不会按预期工作。

To provide additions and clarifications to some of the other answers, if you are using the bulk option for exec or execdir (-exec command {} +), and want to retrieve all the positional arguments, you need to consider the handling of $0 with bash -c.

More concretely, consider the command below, which uses bash -c as suggested above, and simply echoes out file paths ending with '.wav' from each directory it finds:

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

The Bash manual says:

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 positional parameters, starting with $0.

Here, 'echo "$@"' is the command string, and _ {} are the arguments after the command string. Note that $@ is a special positional parameter in Bash that expands to all the positional parameters starting from 1. Also note that with the -c option, the first argument is assigned to positional parameter $0.

This means that if you try to access all of the positional parameters with $@, you will only get parameters starting from $1 and up. That is the reason why Dominik's answer has the _, which is a dummy argument to fill parameter $0, so all of the arguments we want are available later if we use $@ parameter expansion for instance, or the for loop as in that answer.

Of course, similar to the accepted answer, bash -c 'shell_function "$0" "$@"' would also work by explicitly passing $0, but again, you would have to keep in mind that $@ won't work as expected.

冷情妓 2024-10-12 12:30:03

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

Shell 函数位于定义它们的 shell 内部; find 将永远无法看到它们。

Put the function in a separate file and get find to execute that.

Shell functions are internal to the shell they're defined in; find will never be able to see them.

〆一缕阳光ご 2024-10-12 12:30:03

我发现最简单的方法如下,在一个 do 中重复两个命令:

func_one () {
  echo "The first thing with $1"
}

func_two () {
  echo "The second thing with $1"
}

find . -type f | while read file; do func_one $file; func_two $file; done

I find the easiest way is as follows, repeating two commands in a single do:

func_one () {
  echo "The first thing with $1"
}

func_two () {
  echo "The second thing with $1"
}

find . -type f | while read file; do func_one $file; func_two $file; done
夜吻♂芭芘 2024-10-12 12:30:03

不直接,不。 Find 正在单独的进程中执行,而不是在 shell 中执行。

创建一个与您的函数执行相同工作的 shell 脚本,并找到 can -exec

Not directly, no. Find is executing in a separate process, not in your shell.

Create a shell script that does the same job as your function and find can -exec that.

白况 2024-10-12 12:30:03

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

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

I would avoid using -exec altogether. Use xargs:

find . -name <script/command you're searching for> | xargs bash -c
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文