'查找-exec' Linux 中的 shell 函数
有没有办法让 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
由于只有 shell 知道如何运行 shell 函数,因此您必须运行 shell 才能运行函数。您还需要使用
export -f
将函数标记为导出,否则子 shell 将不会继承它们: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:Jac的答案很棒,但有一些问题容易克服的陷阱:
这使用 null 作为分隔符而不是换行符,因此带有换行符的文件名将起作用。它还使用
-r
标志来禁用反斜杠转义,如果没有它,文件名中的反斜杠将不起作用。它还会清除IFS
,以便名称中潜在的尾随空格不会被丢弃。Jac's answer is great, but it has a couple of pitfalls that are easily overcome:
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 clearsIFS
so that potential trailing white spaces in names are not discarded.在
{}
中添加引号,如下所示:这可以纠正由于
find
返回特殊字符而导致的任何错误,例如名称中带有括号的文件。
Add quotes in
{}
as shown below:This corrects any error due to special characters returned by
find
,for example files with parentheses in their name.
批量处理结果
为了提高效率,很多人使用
xargs
来批量处理结果,但是这是非常危险的。因此,find
中引入了一种替代方法来批量执行结果。但请注意,此方法可能会附带一些注意事项,例如 POSIX-
find
中要求在命令末尾包含{}
。find
会将许多结果作为参数传递给一次bash
调用,并且for
循环遍历这些参数,执行函数对其中每一个都做一些事情
。上述解决方案从
$1
开始参数,这就是为什么有_
(代表$0
)。逐一处理结果
以同样的方式,我认为接受的最佳答案应该更正为
这不仅更理智,因为参数应该始终从
$1
开始,而且还使用如果
可能会导致意外行为。find
返回的文件名对 shell 有特殊含义,$0Processing 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 intofind
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.find
will pass many results as arguments to a single call ofbash
and thefor
-loop iterates through those arguments, executing the functiondosomething
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
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 byfind
has special meaning to the shell.只是关于为每个查找结果启动 shell 的已接受答案的警告:
尽管它很好地回答了问题,但它可能不是在查找结果上执行某些代码的最有效方法:
这是 bash 下的所有类型的基准测试解决方案,
包括一个简单的 for 循环情况:
(1465 个目录,在标准硬盘驱动器上,armv7l GNU/Linux synology_armada38x_ds218j)
“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)
"find | while" and "for loop" seems best and similar in speed.
让脚本调用自身,并将找到的每个项目作为参数传递:
当您单独运行脚本时,它会找到您要查找的内容,并调用自身,将每个查找结果作为参数传递。当脚本使用参数运行时,它会执行参数上的命令,然后退出。
Have the script call itself, passing each item found as an argument:
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.
对于那些正在寻找对当前目录中的所有文件执行给定命令的 Bash 函数的人,我从上面的答案中编译了一个:
请注意,它会破坏包含空格的文件名(见下文) )。
举个例子,以这个函数为例:
假设我想将当前目录中所有文件中的所有“hello”实例更改为“world”。我会这样做:
为了安全地处理文件名中的任何符号,请使用:(
但您需要一个处理
-print0
的find
,例如GNUfind
)。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:
Note that it breaks with file names containing spaces (see below).
As an example, take this function:
Say I wanted to change all instances of "hello" to "world" in all files in the current directory. I would do:
To be safe with any symbols in filenames, use:
(but you need a
find
that handles-print0
e.g., GNUfind
).不可能以这种方式执行函数。
为了克服这个问题,您可以将函数放在 shell 脚本中并从
find
中调用它,现在在 find 中使用它,如下所示:
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
Now use it in find as:
如果您使用
exec
或execdir
的批量选项(-exec command {} +),并且想要检索所有位置参数,您需要考虑使用
bash -c
处理$0
。更具体地说,考虑下面的命令,它使用上面建议的 bash -c ,并简单地从它找到的每个目录中回显以“.wav”结尾的文件路径:
Bash 手册说:
这里,
'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
orexecdir
(-exec command {} +
), and want to retrieve all the positional arguments, you need to consider the handling of$0
withbash -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:The Bash manual says:
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 thefor
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.将函数放在一个单独的文件中,然后使用
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.我发现最简单的方法如下,在一个
do
中重复两个命令:I find the easiest way is as follows, repeating two commands in a single
do
:不直接,不。 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.我会完全避免使用
-exec
。使用 xargs:I would avoid using
-exec
altogether. Use xargs: