如何将 shell 命令应用于嵌套(并且转义不佳)子目录中的许多文件? [英] How do I apply a shell command to many files in nested (and poorly escaped) subdirectories?
问题描述
我正在尝试执行以下操作:
I'm trying to do something like the following:
for file in `find . *.foo`
do
somecommand $file
done
但是该命令不起作用,因为 $file 非常奇怪.因为我的目录树有蹩脚的文件名(包括空格),我需要转义 find
命令.但明显的转义似乎都不起作用:-ls
给我空格分隔的文件名片段-fprint
并没有做得更好.
But the command isn't working because $file is very odd. Because my directory tree has crappy file names (including spaces), I need to escape the find
command. But none of the obvious escapes seem to work:
-ls
gives me the space-delimited filename fragments
-fprint
doesn't do any better.
我也试过:for file in "
find . *.foo -ls";做回声$文件;完毕- 但这在一行中给出了来自 find 的所有响应.
I also tried: for file in "
find . *.foo -ls"; do echo $file; done
- but that gives all of the responses from find in one long line.
有什么提示吗?我很高兴有任何解决方法,但我很沮丧,因为我无法解决这个问题.
Any hints? I'm happy for any workaround, but am frustrated that I can't figure this out.
谢谢,亚历克斯
(马特!)
推荐答案
你有很多答案可以很好地解释如何去做;但为了完成起见,我将重复并添加:
You have plenty of answers that explain well how to do it; but for the sake of completion I'll repeat and add to it:
xargs
仅用于交互式使用(当您知道所有文件名都是普通的 - 没有空格或引号)或与 -0
选项一起使用时.否则,它会破坏一切.
xargs
is only ever useful for interactive use (when you know all your filenames are plain - no spaces or quotes) or when used with the -0
option. Otherwise, it'll break everything.
find
是一个非常有用的工具;使用它来将文件名通过管道传输到 xargs
(即使使用 -0
)是相当复杂的,因为 find
可以使用 自己完成所有工作-exec command {} ;
或 -exec command {} +
取决于你想要什么:
find
is a very useful tool; put using it to pipe filenames into xargs
(even with -0
) is rather convoluted as find
can do it all itself with either -exec command {} ;
or -exec command {} +
depending on what you want:
find /path -name 'pattern' -exec somecommand {} ;
find /path -name 'pattern' -exec somecommand {} +
前者在/path
中为每个匹配pattern
的文件递归运行somecommand
和一个参数.
The former runs somecommand
with one argument for each file recursively in /path
that matches pattern
.
后者在/path
中递归地运行与/path
匹配的文件,同时运行somecommand
和尽可能多的参数以适应命令行>模式代码>.
The latter runs somecommand
with as many arguments as fit on the command line at once for files recursively in /path
that match pattern
.
使用哪个取决于somecommand
.如果它可以接受多个文件名参数(例如 rm
、grep
等),那么后一个选项会更快(因为您运行 somecommand
的次数要少得多经常).如果 somecommand
只接受一个参数,那么您需要前一种解决方案.所以看看 somecommand
的手册页.
Which one to use depends on somecommand
. If it can take multiple filename arguments (like rm
, grep
, etc.) then the latter option is faster (since you run somecommand
far less often). If somecommand
takes only one argument then you need the former solution. So look at somecommand
's man page.
有关find
的更多信息:http://mywiki.wooledge.org/UsingFind
More on find
: http://mywiki.wooledge.org/UsingFind
在bash
中,for
是一个迭代参数的语句.如果你这样做:
In bash
, for
is a statement that iterates over arguments. If you do something like this:
for foo in "$bar"
你给了 for
one 参数来迭代(注意引号!).如果你这样做:
you're giving for
one argument to iterate over (note the quotes!). If you do something like this:
for foo in $bar
您要求 bash
获取 bar
的内容,并在有空格、制表符或换行符的地方(从技术上讲, 中的任何字符)将其撕开IFS
) 并使用该操作的片段作为 for 的参数.那不是文件名.假设在一堆文件名中产生包含空格的长字符串的结果是错误的.正如您刚刚注意到的那样.
you're asking bash
to take the contents of bar
and tear it apart wherever there are spaces, tabs or newlines (technically, whatever characters are in IFS
) and use the pieces of that operation as arguments to for. That is NOT filenames. Assuming that the result of a tearing long string that contains filenames apart wherever there is whitespace yields in a pile of filenames is just wrong. As you have just noticed.
答案是:不要使用for
,这显然是错误的工具.上面的find
命令都假设somecommand
是PATH
中的一个可执行文件.如果它是一个 bash
语句,你将需要这个结构(迭代 find
的输出,就像你试过的那样,但安全):
The answer is: Don't use for
, it's obviously the wrong tool. The above find
commands all assume that somecommand
is an executable in PATH
. If it's a bash
statement, you'll need this construct instead (iterates over find
's output, like you tried, but safely):
while read -r -d ''; do
somebashstatement "$REPLY"
done < <(find /path -name 'pattern' -print0)
这使用了一个 while-read
循环,它读取 find
输出的字符串的一部分,直到它到达 NULL
字节(这就是 -print0
用于分隔文件名).由于 NULL
字节不能成为文件名的一部分(与空格、制表符和换行符不同),这是一个安全的操作.
This uses a while-read
loop that reads parts of the string find
outputs until it reaches a NULL
byte (which is what -print0
uses to separate the filenames). Since NULL
bytes can't be part of filenames (unlike spaces, tabs and newlines) this is a safe operation.
如果您不需要 somebashstatement
成为脚本的一部分(例如,它不会通过保持计数器或设置变量等来改变脚本环境),那么您仍然可以使用 find
的 -exec
来运行你的 bash
语句:
If you don't need somebashstatement
to be part of your script (eg. it doesn't change the script environment by keeping a counter or setting a variable or some such) then you can still use find
's -exec
to run your bash
statement:
find /path -name 'pattern' -exec bash -c 'somebashstatement "$1"' -- {} ;
find /path -name 'pattern' -exec bash -c 'for file; do somebashstatement "$file"; done' -- {} +
这里,-exec
执行带有三个或更多参数的 bash
命令.
Here, the -exec
executes a bash
command with three or more arguments.
- 要执行的 bash 语句.
- 一个
--
.bash
会把它放在$0
中,你可以在这里放任何你喜欢的东西,真的. - 您的一个或多个文件名(取决于您分别使用的是
{} ;
还是{} +
).文件名以$1
结尾(和$2
、$3
,...当然,如果有多个).
- The bash statement to execute.
- A
--
.bash
will put this in$0
, you can put anything you like here, really. - Your filename or filenames (depending on whether you used
{} ;
or{} +
respectively). The filename(s) end(s) up in$1
(and$2
,$3
, ... if there's more than one, of course).
此处第一个 find
命令中的 bash
语句以文件名作为参数运行 somebashstatement
.
The bash
statement in the first find
command here runs somebashstatement
with the filename as argument.
此处第二个 find
命令中的 bash
语句运行了一个 for
(!) 循环每个位置参数(这就是简化的 for
语法 - for foo; do
- 所做的)并运行一个 somebashstatement
并将文件名作为参数.我用 -exec {} +
展示的第一个 find
语句之间的区别在于,我们只为许多文件名运行一个 bash
进程但对于这些文件名中的每个,仍然有一个somebashstatement
.
The bash
statement in the second find
command here runs a for
(!) loop that iterates over each positional parameter (that's what the reduced for
syntax - for foo; do
- does) and runs a somebashstatement
with the filename as argument. The difference here between the very first find
statement I showed with -exec {} +
is that we run only one bash
process for lots of filenames but still one somebashstatement
for each of those filenames.
所有这些在上面链接的 UsingFind
页面中也有很好的解释.
All this is also well explained in the UsingFind
page linked above.
这篇关于如何将 shell 命令应用于嵌套(并且转义不佳)子目录中的许多文件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!