如何将 shell 命令应用于嵌套(并且转义不佳)子目录中的许多文件? [英] How do I apply a shell command to many files in nested (and poorly escaped) subdirectories?

查看:22
本文介绍了如何将 shell 命令应用于嵌套(并且转义不佳)子目录中的许多文件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试执行以下操作:

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.如果它可以接受多个文件名参数(例如 rmgrep 等),那么后一个选项会更快(因为您运行 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 命令都假设somecommandPATH 中的一个可执行文件.如果它是一个 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.

  1. 要执行的 bash 语句.
  2. 一个--.bash 会把它放在 $0 中,你可以在这里放任何你喜欢的东西,真的.
  3. 您的一个或多个文件名(取决于您分别使用的是 {} ; 还是 {} +).文件名以 $1 结尾(和 $2$3,...当然,如果有多个).
  1. The bash statement to execute.
  2. A --. bash will put this in $0, you can put anything you like here, really.
  3. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆