我该如何申请一个shell命令的许多文件嵌套(和逃脱很差)子目录? [英] How do I apply a shell command to many files in nested (and poorly escaped) subdirectories?
问题描述
我试图做类似以下内容:
在`找到文件。 * .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 $ C $的可执行文件C>。如果它是一个
庆典
语句,你需要这个结构来代替(超过迭代发现的
输出,像你这样的尝试但安全):
而读-r -d'';做
somebashstatement$ REPLY
完成< ≤(查找/ -name路径'模式'-print0)
这使用了而读
循环读取字符串找到
输出部分,直到它到达一个 NULL
字节(这是 -print0
用来分隔文件名)。由于 NULL
字节数不能文件名的一部分(不同于空格,制表符和换行符),这是一个安全的操作。
如果您不需要 somebashstatement
来是你的脚本的一部分(例如,它不保持计数器或设置一个变量或一些改变脚本环境这样的),那么你仍然可以使用找到的
-exec
来运行庆典
语句:
查找/ -name路径'模式'-exec bash的-c'somebashstatement$ 1' - {} \\;
查找/ -name路径'模式'-exec bash的-c'文件;做somebashstatement$文件;完成 - {} +
在这里, -exec
执行三个或三个以上参数的庆典
命令。
- bash的语句来执行。
- A
-
。庆典
会把这个在$ 1,0
,你可以把任何你喜欢这里,真的。 - 您的文件名或文件名(取决于你是否使用
{} \\;
或{} +
分别)。文件名(S)端(S)起来$ 1
(和$ 2
,$ 3
,...如果有不止一个,当然)。
在第一个 在这里的第二个 这一切也很好在上文中 I'm trying to do something like the following: 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 I also tried: 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: The former runs The latter runs Which one to use depends on More on In you're giving you're asking The answer is: Don't use This uses a If you don't need Here, the The The All this is also well explained in the 这篇关于我该如何申请一个shell命令的许多文件嵌套(和逃脱很差)子目录?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!找到
命令在这里运行 somebashstatement $ c中的
庆典
语句$ C>与文件名作为参数。</ p>找到
命令庆典
语句运行为
( !)循环,在每个位置参数迭代(这是减少什么为
语法 - foo的;做好
- 那样)并运行 somebashstatement
与文件名作为参数。在此之间的区别的第一找到
语句我表现出与 -exec {} +
的是,我们只运行一个庆典
的文件名的过程,大量的文件名,但仍然有一个 somebashstatement
对于每个 UsingFind
页解释说。for file in `find . *.foo`
do
somecommand $file
done
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"; do echo $file; done
- but that gives all of the responses from find in one long line.
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 {} +
somecommand
with one argument for each file recursively in /path
that matches pattern
.somecommand
with as many arguments as fit on the command line at once for files recursively in /path
that match pattern
.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/UsingFindbash
, for
is a statement that iterates over arguments. If you do something like this:for foo in "$bar"
for
one argument to iterate over (note the quotes!). If you do something like this:for foo in $bar
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
, 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
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
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
executes a bash
command with three or more arguments.
--
. bash
will put this in $0
, you can put anything you like here, really.{} \;
or {} +
respectively). The filename(s) end(s) up in $1
(and $2
, $3
, ... if there's more than one, of course).bash
statement in the first find
command here runs somebashstatement
with the filename as argument.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
page linked above.