模棱两可的重定向

时间:2012-04-19 19:14:02

标签: linux bash redirect

我正在尝试使用当前目录和所有子目录,并在每个以.sql结尾的文件中添加一些注释

这是一段代码

HEADER="--SQL HEADER"

for f in 'find . -name *.sql';
do
        echo $f
        echo -e $HEADER > $f.tmp;
        FNAME=${f//\//_/};
        echo -e "\n\n--MORE ANNOTATIONS ${FNAME%.*}:1" >> $f.tmp;
        cat $f >> $f.tmp;
        mv $f.tmp $f;
        rm $f.tmp
done;

我是bash的初学者所以我认为我得到的一些错误可能是由于循环的find语句 但这是我得到的错误

find . -name X.sql A.sql W.sql E.sql S.sql
./annotate.sh: line 6: $f.tmp: ambiguous redirect
./annotate.sh: line 8: $f.tmp: ambiguous redirect
./annotate.sh: line 9: $f.tmp: ambiguous redirect
mv: invalid option -- n
Try `mv --help' for more information.
rm: invalid option -- n
Try `rm --help' for more information.

任何帮助将不胜感激=)

2 个答案:

答案 0 :(得分:3)

这是问题所在。你的“回声”让它消失了:

echo $f

输出

find . -name X.sql A.sql W.sql E.sql S.sql

我认为问题是你在find命令中有单引号(''),而不是反引号(``)。所以它不是真正运行查找,而是简单地扩展通配符。

您可能必须引用通配符,以便将其传递给find而不是由shell评估:

for f in `find . -name \*.sql`;

但是,您的脚本中存在一些问题,如果您想多次使用它,则应该解决这些问题。请参阅ormaaj's answer

答案 1 :(得分:2)

正如已经指出的那样,问题是查找实际上并未执行。但是,这种模式非常错误。使用for循环对命令替换发生的任何事情进行迭代都不起作用,因为将输出拆分为单词需要进行单词拆分,这需要不引用,即使禁用路径名扩展也是一个问题,因为文件名可以包含换行符。 / p>

最好使用-exec。首先将此脚本写入文件chmod u+x scriptname

#!/usr/bin/env bash
header="--SQL HEADER"

for f in "$@"; do
    echo "$f" >&2
    fname=${f//\//_/}

    cat - "$f" <<EOF >"$f.tmp"
    ${header}$'\n\n'
    --MORE ANNOTATIONS ${fname%.*}:1
    EOF

    mv "$f.tmp" "$f"
done

然后像这样运行find

find . -name '*.sql' -exec scriptname {} +

或者,(并假设这是Bash的最新版本),使用globstar而不是find(如果您愿意,ksh具有类似的功能)。根据作业,这可能会更慢 - shell必须预先生成文件列表。

#!/usr/bin/env bash
shopt -s globstar

for f in ./**/*.sql; do
    ...

或者,如果您有Bash 4和具有必要GNU实用程序的系统,请使用-print0

find . -name '*.sql' -print0 | while IFS= read -rd '' f; do
    # <body of the above for loop here>
done

请参阅:http://mywiki.wooledge.org/UsingFind