2013-02-22 26 views
11

我有一个脚本只能将数据写入stdout。我需要运行它为多个文件,并为每个输入文件生成不同的输出文件,我想知道如何使用find -exec。所以我基本上尝试的这几款变种(我更换了剧本由cat只是测试性的目的):使用find -exec重定向stdout并且不创建新的shell

找到*型的F -exec猫 “{}”> “{} .stdout” \;

但由于所有的数据都被写入一个字面上名为{}.stdout的文件,所以无法工作。

最后,我可以把它一起工作:

发现*型的F -exec SH -c “猫{}> {} .stdout” \;

不过,虽然这种最新形式与cat效果很好,我的脚本需要通过几个初始化脚本加载环境变量,因此,我结束了:

发现*型的F -exec SH -c“ initscript1; initscript2; ...; myscript {}> {} .stdout“\;

这似乎是一种浪费,因为我已经在我的当前shell中初始化了一切。

有没有更好的方法来做到这一点find?其他单线也欢迎。

+2

如果它们在原始shell中初始化,但未在子shell中设置,则它们不是环境变量。在你的脚本的顶部写上'set -a'。 – 2013-02-22 18:23:25

+0

你给出的最后一个例子是正确的还是命令:'find。 -type f -exec sh -c“。initscript1;。initscript2; ...; myscript {}> {} .stdout”\; '(而不是简单地调用'initscript1',你实际上是否在调用'.initscript1',即你正在使用点命令来源文件)。 – 2013-02-22 18:30:11

回答

5

简单的解决办法是把一个包装器脚本:

#!/bin/sh 

myscript "$1" > "$1.stdout" 

说它myscript2与查找调用它:

find . -type f -exec myscript2 {} \; 

注意,虽然大多数实现找到让你做你所做的事情,从技术上来说,如果在-exec的参数列表中多次使用{},那么find的行为是未指定的。

+2

但是在'find'手册中,在'-exec'的某个地方有这样的说法:_字符串'{}'被替换为当前文件名,无论它出现在命令的参数中的任何地方,就像在某些版本的find._ [link](http://unixhelp.ed.ac.uk/CGI/man-cgi?find)中一样。不过,感谢您的解决方法。 – jserras 2013-02-22 22:14:38

+3

您的'find'的特定实现手册声明它的工作原理,但标准内容如下:'如果包含两个字符的多个参数“{}”存在,则行为未指定。“这不是什么大问题,但它可能会烧你(在这一点上,它突然变得非常重要!) – 2013-02-22 22:32:02

+3

一个更重要的缺点是,诸如'-exec sh -c“myscript {}> {} .stdout”\;'可能导致面对恶意文件名的任意代码执行。执行'-exec sh -c'myscript“$ 1”>“$ 1.stdout”'sh {} \;'会更安全。 – jilles 2013-02-22 23:50:38

2

你可以用eval来做到这一点。它可能很难看,但为此必须制作一个shell脚本。另外,这一切都在一条线上。 例如

find -type f -exec bash -c "eval md5sum {} > {}.sum " \; 
+0

'bash -c'是这里的牛肉,'eval'实际上并没有做任何有用的事情。但是你并没有躲避外壳。 – tripleee 2017-03-21 16:00:22

+0

如果你拿出'eval',我认为这应该是实际上被接受的答案,尽管OP会放弃避开壳。 (将脚本放在单独的文件中时,无论如何都要在运行该脚本时创建一个shell,OP所要求的并不是真的可能。) – tripleee 2017-03-21 16:02:30

+0

“eval”在这里非常危险。如果你有一个包含'$(rm -rf $ HOME)'的文件名,这将会是非常糟糕的消息。 – 2017-04-05 17:27:48

2

如果出口你的环境变量,他们就已经存在于子shell(如果使用bash -c代替sh -c,和您的父母壳本身的bash,那么你也可以在父shell中导出函数,并将它们用于子项中;请参阅export -f)。

此外,通过使用-exec ... {} +,你可以限制弹所需的数量尽可能少的数量,通过在命令行上所有参数:

set -a # turn on automatic export of all variables 
source initscript1 
source initscript2 

# pass as many filenames as possible to each sh -c, iterating over them directly 
find * -name '*.stdout' -prune -o -type f \ 
    -exec sh -c 'for arg; do myscript "$arg" > "${arg}.stdout"' _ {} + 

或者,你可以在你的当前执行的执行直接壳:

while IFS= read -r -d '' filename; do 
    myscript "$filename" >"${filename}.out" 
done < <(find * -name '*.stdout' -prune -o -type f -print0) 

参见UsingFind安全地讨论和通过find正确地履行批量操作;和BashFAQ #24讨论使用进程替换(<(...)语法)以确保操作在父shell中执行。

+0

使用'_'作为$ 0来调用sh有点混淆! – 2017-04-05 17:50:06

+0

@WilliamPursell,这是一个常见的成语 - 可以找到链接,如果你喜欢。 ('_'在其他一些语言中也是一个常规的未使用/占位符值,比如Python,但我的理解是它首先在shell中很常见)。 – 2017-04-05 18:01:04

+0

我已经看到它在go和perl中使用,但从未在此设置中使用过。我倾向于忽略它,并将$ 0设置为{},这可能是一种非常糟糕的做法! – 2017-04-05 18:40:02

相关问题