我该如何申请一个shell命令的许多文件嵌套(和逃脱很差)子目录? [英] How do I apply a shell command to many files in nested (and poorly escaped) subdirectories?

查看:162
本文介绍了我该如何申请一个shell命令的许多文件嵌套(和逃脱很差)子目录?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图做类似以下内容:

 在`找到文件。 * .foo`

somecommand $文件
DONE

但命令不工作,因为$文件是非常奇怪的。因为我的目录树有蹩脚的文件名(包括空格),我需要逃避找到命令。但没有明显的逃逸似乎工作:
   -ls 给我的空间分隔的文件名片段
   -fprint 不做任何好转。

我也试过:在文件找*包含.foo -ls 。做回声$文件; DONE
- 但是,让所有从发现一个长行的答复。

任何提示?我很高兴的任何解决办法,但很沮丧,我不明白这一点。

谢谢,
亚历克斯

(嗨马特!)


解决方案

您有足够的解释以及如何做到这一点的答案;但完成的缘故,我会重复,并添加到它:

的xargs 是交互使用只有永远有用的(当你知道你所有的文件名是普通的 - 没有空格或引号),或与使用时 -0 选项。否则,它会打破一切。

找到是一个非常有用的工具;把它用管道将文件名的xargs (甚至与 -0 )是颇为曲折的找到可与 -exec命令{} \\做到这一切本身; -exec命令{} + 取决于你想要什么:

 查找/ -name路径'模式'-exec somecommand {} \\;
查找/ -name路径'模式'-exec somecommand {} +

前者运行 somecommand 一个参数作为递归 /路径中每个文件匹配模式

后者运行 somecommand 与递归在 / 许多参数作为适合在命令行上是在一次文件路径那场比赛模式

使用哪一个取决于 somecommand 。如果可以把多​​个文件名参数(如 RM 的grep 等),那么后者的选择是快(因为您运行 somecommand 以前那么频繁了)。如果 somecommand 只有一个参数,那么你需要前者的解决方案。所以看 somecommand 的手册页。

更多关于找到 http://mywiki.wooledge.org/UsingFind

庆典是遍历参数的声明。如果你做这样的事情:

 在$酒吧福

你给 有一个参数来遍历(注意引号!)。如果你做这样的事情:

 在$ foo的酒吧

你问庆典栏的内容键,把它拆开哪里有空格,制表符或换行符(技术上,无论字符是 IFS ),并使用该操作的部分作为参数传递给了。 ,是不是文件名即可。假设一个撕裂一长串包含结果档案名称出现在一堆文件名的空格相隔收益率无论是绝对错误的。正如你刚才注意到了。

答案是:不要使用,这显然是错误的工具。以上找到命令都假定 somecommand PATH 。如果它是一个庆典语句,你需要这个结构来代替(超过迭代发现的输出,像你这样的尝试但安全):

 而读-r -d'';做
    somebashstatement$ REPLY
完成< ≤(查找/ -name路径'模式'-print0)

这使用了而读循环读取字符串找到输出部分,直到它到达一个 NULL 字节(这是 -print0 用来分隔文件名)。由于 NULL 字节数不能文件名的一部分(不同于空格,制表符和换行符),这是一个安全的操作。

如果您不需要 somebashstatement 来是你的脚本的一部分(例如,它不保持计数器或设置一个变量或一些改变脚本环境这样的),那么你仍然可以使用找到的 -exec 来运行庆典语句:

 查找/ -name路径'模式'-exec bash的-c'somebashstatement$ 1' -  {} \\;
查找/ -name路径'模式'-exec bash的-c'文件;做somebashstatement$文件;完成 - {} +

在这里, -exec 执行三个或三个以上参数的庆典命令。


  1. bash的语句来执行。

  2. A - 庆典会把这个在 $ 1,0 ,你可以把任何你喜欢这里,真的。

  3. 您的文件名或文件名(取决于你是否使用 {} \\; {} + 分别)。文件名(S)端(S)起来 $ 1 (和 $ 2 $ 3 ,...如果有不止一个,当然)。

在第一个找到命令在这里运行 somebashstatement 庆典语句$ C>与文件名作为参数。<​​/ p>

在这里的第二个找到命令庆典语句运行 !)循环,在每个位置参数迭代(这是减少什么语法 - foo的;做好 - 那样)并运行 somebashstatement 与文件名作为参数。在此之间的区别的第一找到语句我表现出与 -exec {} + 的是,我们只运行一个庆典的文件名的过程,大量的文件名,但仍然有一个 somebashstatement 对于每个

这一切也很好在上文中 UsingFind 页解释说。

I'm trying to do something like the following:

for file in `find . *.foo`
do
somecommand $file
done

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.

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.

Thanks, Alex

(Hi Matt!)

解决方案

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 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 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 {} +

The former runs somecommand with one argument for each file recursively in /path that matches pattern.

The latter runs somecommand with as many arguments as fit on the command line at once for files recursively in /path that match pattern.

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.

More on find: http://mywiki.wooledge.org/UsingFind

In bash, for is a statement that iterates over arguments. If you do something like this:

for foo in "$bar"

you're giving for one argument to iterate over (note the quotes!). If you do something like this:

for foo in $bar

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.

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)

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.

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' -- {} +

Here, the -exec executes a bash command with three or more arguments.

  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).

The bash statement in the first find command here runs somebashstatement with the filename as argument.

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.

All this is also well explained in the UsingFind page linked above.

这篇关于我该如何申请一个shell命令的许多文件嵌套(和逃脱很差)子目录?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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