5

I'm trying to write a code search script, with configurable directories and files that are excluded. The main part of this script is the following line:

out=$(grep -TInr$C$W --color=always \
    --exclude={translations} \
    --exclude-dir={build,tmp} \
    "$SEARCH")

The $C and $W variables are set by script parameters to configure case insensitivity and exact word matching, and $SEARCH is simply the remaining parameters, which will be the search string. However, my implementation to ignore certain files does not quite work yet.

To exclude files from search, I'm trying to use a ~/.codesearch_config file, which looks like this:

#!/bin/bash

if [[ $PWD == "$HOME/project"* ]]; then
    exclude_directories={.git,build,tmp}
    exclude_files={translations}
fi

The idea here, of course, is that depending on your current working directory, a certain set of excludes will be loaded. But when trying to add these to the script as such:

--exclude=$exclude_files

the whole parameter will be put in single quotes by bash (tested with the -x option to debug) like this:

grep -TInrw --color=always '--exclude={translations}' '--exclude-dir={build,tmp}' search_term

What I want it to do is expand that to --exclude-dir=build --exclude-dir=tmp. If I manually add the values of those $exclude_ variables to the command, it expands just fine; The issue is just that the single quotes are placed around my parameter and glob. How can I prevent this?

  • 2
    Brace expansion happens before variable expansion. You should look for another way. – muru Apr 4 '16 at 11:29
  • 1
    You can use eval to expand the whole line before executing it, e.g. $out=(eval "grep ..."), however, this works only if your input is trusted. – galaxy Apr 4 '16 at 12:15
  • @muru: Would you have any suggestions as to what other way this would be possible? – Erik S Apr 4 '16 at 13:19
  • 1
    you could try using an array for exclude_directories and exclude_files (e.g. exclude_directories=(.git build tmp), and then expand them into --exclude-dir and --exclude options (note the plural in options, one for each element of the arrays) in a loop later. – cas Apr 4 '16 at 13:32
3

Try using arrays for your exclusions, and expand them into --exclude-dir and --exclude options.

e.g. in your ~/.codesearch_config script (presumably this is sourced by your main script?):

#! /bin/bash

# temporary array variables
declare -a __exclude_directories
declare -a __exclude_files

if [[ "$PWD" == "$HOME/project"* ]]; then
    __exclude_directories=(.git build tmp)
    __exclude_files=(translations)
elif [[ "$PWD" == "/some/where/else" ]]; then
    __exclude_directories=(foo bar)
    __exclude_files=(abc.txt def.txt xyz.txt)
fi

exclude_directories=''
exclude_files=''

for i in "${__exclude_directories[@]}" ; do
    exclude_directories+=" --exclude-dir '$i'"
done

for i in "${__exclude_files[@]}" ; do
    exclude_files+=" --exclude '$i'"
done

unset __exclude_directories
unset __exclude_files

# comment out or delete the next two lines after you've verified
# that the output is correct.
echo $exclude_directories 
echo $exclude_files

Later, you would use them like this:

out=$(grep -TInr$C$W --color=always \
      $exclude_files \
      $exclude_directories \
      "$SEARCH")

Note: there are no quotes around the $exclude_* variables here, otherwise they will be treated as a single argument each rather than multiple --exclude and --exclude-dir arguments. This is one of the very few situations where you do not want to and should not double-quote your variables (i.e. when you are constructing a command line in a variable or variables).

In almost all other cases, you should, as a matter of deeply-ingrained habit, double-quote your variables.

  • Thank you for the in-depth explanation. It's actually what I wrote based on your comment, so I'm glad I have something to accept now! – Erik S Apr 4 '16 at 14:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.