使用bash在子目录中运行命令

时间:2016-07-12 19:36:17

标签: bash shell

我有一系列目录,我需要运行各种shell命令,并且我已经制作了一个名为dodirs.sh的简短脚本,以简化在每个目录中运行命令的步骤:

#!/bin/bash
echo "Running in each directory: $@"
for d in ./*/; do
    (
    cd "$d"
    pwd
    eval "$@"
)
done

对于许多简单命令来说这很好,但有些有问题,例如:

grep "free  energy   TOTEN" OUTCAR | tail -1

在每个目录中的文件中查找字符串。

似乎管道和/或报价是麻烦,因为如果我说:

dodirs.sh grep "free  energy   TOTEN" OUTCAR

我得到一个合理的(如果是长期输出):

Running in each directory: grep free  energy   TOTEN OUTCAR
...
OUTCAR:  free energy    TOTEN  =      -888.53122906 eV
OUTCAR:  free energy    TOTEN  =      -888.53132396 eV
OUTCAR:  free  energy   TOTEN  =      -888.531324 eV
...

我注意到echo的结果丢失了引号,所以这有点奇怪。另一方面,如果我说:

dodirs.sh grep "free  energy   TOTEN" OUTCAR | tail -1

然后我得到了荒谬的说法:

...
grep: energy: No such file or directory
grep: TOTEN: No such file or directory
...

请注意,现在回声根本没有回显,显然是错误解释了这条线。

我是否有某种方法可以转义字符,或将参数打包到我的dodirs.sh脚本中?

也许有人知道一个更好的方法?

2 个答案:

答案 0 :(得分:0)

引号消失,因为一旦shell识别要作为参数传递给脚本的单词,它们就不再需要了。在您的脚本中,$1grep$2free energy TOTEN等。

您确实需要转义管道(使用反斜杠\|或引用'|'),以便作为参数传递给{{ 1}}。

eval

答案 1 :(得分:0)

考虑:

#!/bin/bash

# use printf %q to generate a command line identical to what we're actually doing
printf "Running in each directory: " >&2
printf '%q ' "$@" >&2
echo >&2

# use && -- we don't want to execute the command if cd into a given directory failed!
for d in ./*/; do
    (cd "$d" && echo "$PWD" >&2 && "$@")
done

这更加可预测:它通过精确参数列表,因此对于一般命令,您可以自然地引用它。 (这与使用find -exec或其他调用execv*的工具完全相同 - 使用文字传递参数列表进行家庭调用;因此,这意味着您获得与{{{{{{{{ 1}},sudochpstchroot等。)

对于单个命令,调用看起来像您期望的那样:

setsid

要执行shell指令(例如管道),请显式执行shell:

dodirs grep "free  energy   TOTEN" OUTCAR

...或者,如果您愿意让调用者依赖于实现细节(例如,这是通过shell实现的,并且确切地 shell' s实现了),使用dodirs sh -c 'grep "free energy TOTEN" OUTCAR | tail -n 1' # ^^ ^^

eval

这可能稍微多一些工作,但它使您符合标准的UNIX约定,并且如果调用者未能引用他们的参数dodirs eval 'grep "free energy TOTEN" OUTCAR | tail -n 1' # ^^^^ - 安全,则可以避免冒着shell注入漏洞的风险。