Home > Back-end >  Bash wrapping parts of a variable in quotes when expanded
Bash wrapping parts of a variable in quotes when expanded

Time:11-16

I'm trying to recursively find c and header files in a script, while avoiding globbing out any that exist in the current directory.

FILE_MATCH_LIST='"*.c","*.cc","*.cpp","*.h","*.hh","*.hpp"'
FILE_MATCH_REGEX=$(echo "$FILE_MATCH_LIST" | sed 's/,/ -o -name /g')
FILE_MATCH_REGEX="-name $FILE_MATCH_REGEX"

This does exactly what I want it to:

   FILE_MATCH_REGEX='-name "*.c" -o -name "*.cc" -o -name "*.cpp" -o -name "*.h" -o -name "*.hh" -o -name "*.hpp"'

Now, if I call find with that string (in quotes), it maintains the leading and trailing quotes and breaks find:

files=$(find $root_dir "$FILE_MATCH_REGEX" | grep -v $GREP_IGNORE_LIST)

  find [directory] '-name "*.c" -o -name "*.cc" -o -name "*.cpp" -o -name "*.h" -o -name "*.hh" -o -name "*.hpp"'

This results in a "unknown predicate" error from find, because the entire predicate is single quoted.

If I drop the quotes from the variable in the find command, I get a strange behavior:

files=$(find $root_dir $FILE_MATCH_REGEX | grep -v $GREP_IGNORE_LIST)

   find [directory] -name '"*.c"' -o -name '"*.cc"' -o -name '"*.cpp"' -o -name '"*.h"' -o -name '"*.hh"' -o -name '"*.hpp"'

Where are these single quotes coming from? They exist if I echo that variable as well, but they aren't there in the command when I'm actually setting the $FILE_MATCH_REGEX (As seen at the beginning of the question).

This of course also breaks find, because it's looking for the actual double quoted string, instead of expanding the *.h etc.

How do I get these strings into find without all of these quoting woes?

CodePudding user response:

Fleshing out the array answer:

#!/bin/bash
patterns=( '*.c' '*.cc' '*.h' '*.hh' )
find_args=( "-name" "${patterns[0]}" )
for (( i=1 ; i < "${#patterns[@]}" ; i   )) ; do
  find_args =( "-o" "-name" "${patterns[i]}" )
done
find [directory] "${find_args[@]}"

Also, to clear up the misconception around quotes, if you echo the last line the output might not be what you expect:

echo find [directory] "${find_args[@]}"
# outputs: find [directory] -name *.c -o -name *.cc -o -name *.h -o -name *.hh

Where are the quotes? Your shell removed them after it was done with them. Quotes are not find syntax, they are shell syntax that tell the shell how to interpret (or perhaps how NOT to interpret) your command line.

The reason for the strange behavior in your debug output is that the quotes in your data are literal quotes, not shell syntax quotes that get removed during command parsing. The debugger is just trying to point out the distinction.

Some useful resources on the Bash wiki:

  • BashParser explains how your command line gets parsed and executed
  • BashFAQ/050 explains why embedding quotes in your data isn't sufficient

CodePudding user response:

If you have GNU find - adjust to your liking:

#!/bin/bash

#FILE_MATCH_LIST='"*.c","*.cc","*.cpp","*.h","*.hh","*.hpp"'
FILE_MATCH_LIST='.*/.*\.(c|cc|cpp|h|hh|hpp)'

find . -type f -regextype posix-egrep -regex "${FILE_MATCH_LIST}"
  • Related