user766353
user766353

Reputation: 537

Why does grep ignore the shell variable containing directories to be ignored?

On Mac OS X, I have a bash script like this:

# Directories excluded from grep go here.
EXCLUDEDIR="--exclude-dir={node_modules,.git,tmp,angular*,icons,server,coffee}"

# This grep needs to include one line below the hit.
grep -iIrn -A1 $EXCLUDEDIR -e "class=[\"\']title[\"\']>$" -e "<div class=\"content" . > microcopy.txt

but it seems to be ignoring $EXCLUDEDIR. If I simply use the --exclude-dir directly, it works. Why won't it expand the variable and work right?

Upvotes: 2

Views: 508

Answers (2)

Kyle Strand
Kyle Strand

Reputation: 16529

@tripleee correctly describes the problem, but there are two workarounds that I think are simpler (and, I think, more portable) than using an array: use eval in the git command, or use echo in the variable assignment itself. The echo method is preferable.

Using eval

# Directories excluded from grep go here.
EXCLUDEDIR="--exclude-dir={node_modules,.git,tmp,angular*,icons,server,coffee}"

# This grep needs to include one line below the hit.
eval grep -iIrn -A1 $EXCLUDEDIR # .... etc

This causes the braces to be expanded as if they had been typed literally. Note, however, that it may have some unintended side-effects if you're not careful; for instance, you may need to add some extra \'s to escape quotes and $-signs.

Using echo

This is potentially safer than eval, since you won't accidentally execute code hidden in the EXCLUDEDIR variable.

# Directories excluded from grep go here.
EXCLUDEDIR="$(echo --exclude-dir={node_modules,.git,tmp,angular*,icons,server,coffee})"

# This grep needs to include one line below the hit.
grep -iIrn -A1 $EXCLUDEDIR # .... etc

Upvotes: 2

tripleee
tripleee

Reputation: 189936

The braces are technically an error. When they are in a variable, they are included verbatim, while when you type them directly as part of the command, Bash performs brace expansion, and effectively removes the braces from your expression.

bash$ echo --exclude-dir=moo{bar,baz}
--exclude-dir=moobar --exclude-dir=moobaz

bash$ x='moo{bar,baz}'
bash$ echo --exclude-dir=$x
--exclude-dir=moo{bar,baz}

The (not so simple) workaround is to list your parameters explicitly instead. This can be somewhat simplified by using an array to list the directory names you want to exclude (but this is not portable to legacy /bin/sh).

x=(node_modules .git tmp angular\* icons server coffee)
EXCLUDEDIR="${x[@]/#/--exclude-dir=}"

The backslash in angular\* is to pass this wildcard expression through to grep unexpanded -- if the shell would expand the variable, grep would not exclude directories matching the wildcard expression in subdirectories (unless they conveniently happened to match one of the expanded values in the current directory). If you have nullglob in effect, an unescaped wildcard would simply disappear from the lists.

Upvotes: 6

Related Questions