Pita
Pita

Reputation: 1474

ambiguous redirection

I'm trying to go the current directory and all sub direcotires, and add some annotations into each file that ends in .sql

heres a snippet of the code

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;

im a beginner at bash so i think some of the errors im getting might be due to the find statement with the loop but this is the error i get

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.

any help would be greatly appreciated =)

Upvotes: 0

Views: 5466

Answers (2)

theglauber
theglauber

Reputation: 29615

Here's the problem. Your "echo" gives it away:

echo $f

outputs

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

I think the problem is you have straight single quotes ('') in the find command, instead of backquotes (``). So it's not really running find, but simply expanding the wildcards.

You may have to quote the wildcard so it gets passed to find instead of evaluated by the shell:

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

However, there are several problems in your script, which you should address if you want to use it more than once. See ormaaj's answer.

Upvotes: 3

ormaaj
ormaaj

Reputation: 6577

The problem, as already pointed out, is that find isn't actually being executed. However, this pattern is very wrong. Iterating using a for loop over anything that happens with a command substitution doesn't work because splitting the output into words requires word-splitting, which requires not quoting, which is a problem even if pathname expansion is disabled because filenames can contain newlines.

Preferably, use -exec. First write this script to a file and 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

Then run find like this:

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

Alternatively, (and assuming this is a recent version of Bash), use globstar and no find (ksh has a similar feature if you prefer). This may be slower depending upon the job - the shell must pre-generate the list of files.

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

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

Alternatively, if you have Bash 4 and a system with the necessary GNU utilities, use -print0.

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

See: http://mywiki.wooledge.org/UsingFind

Upvotes: 2

Related Questions