1

I would like to save egrep output to a bash array:

arr=( $(egrep -Rn 'regex') )

If there so happens to be a '*' in the egrep result, it appears like bash is expanding the '*' to be all files in current directory. And the expansion plus results of egrep are then saved into arr.

How do I fix this? I want the '*' in the grep results to be unaltered.

6
  • 5
    Don't stick results into an array like this. It has exactly this (and other word-splitting issues). See mywiki.wooledge.org/BashFAQ/001 for how to properly (and safely) read line-by-line or field-by-field. Commented May 26, 2015 at 14:32
  • 1
    Alternatively, if you really just wanted to avoid the globbing you could turn globbing off with set -f (but this leaves the word-splitting issues/etc. affecting the assignment and isn't recommended as-such). Commented May 26, 2015 at 14:34
  • Okay, I'll look into your page. But, I need to manipulate the results later, ask the user what he wants and continuing from there. So I thought an array would be good for this? Commented May 26, 2015 at 14:39
  • 2
    I didn't say don't use an array. I said don't populate your array like this. See the linked page. Commented May 26, 2015 at 14:45
  • Hmm. Shame this doesn't have an entry on the BashPitfalls page (mywiki.wooledge.org/BashPitfalls), being as common of a mistake as it is. Commented May 26, 2015 at 14:53

1 Answer 1

2

To use the idiom attempted in the question "correctly" might look something like this:

# DON'T DO THIS.

set -f         # turn off globbing
IFS=$'\n'      # word-split only on newlines
arr=( $(...) ) # populate array
unset IFS      # return IFS to defaults (assuming it was there before)
set +f         # turn globbing back on

Obviously, there's a lot of room to get this wrong and leave your shell in a state other than the way it started (What if your script had a different initial IFS value? What if this code is sourced from a script that wants globbing to be disabled to work correctly?). Don't do it.


One approach, compatible with bash 3.x, is to use read -a (reading into an array) with IFS (used to separate fields) containing a newline, and -d (used to separate records) set to a NUL:

IFS=$'\n' read -r -d '' -a arr < <(egrep -Rn 'regex' && printf '\0')

The trailing NUL added to the input is present to ensure that read exits successfully; otherwise, this could trigger an abrupt exit if using set -e.


A longer but more explicit approach is to do the iteration yourself:

arr=( )
while IFS= read -r; do
  arr+=( "$REPLY" )
done < <(egrep -Rn 'regex')

Another, using bash 4.x features (readarray, AKA mapfile):

readarray -t arr < <(egrep -Rn 'regex')
Sign up to request clarification or add additional context in comments.

4 Comments

Hey this is cool. Thanks! I'm using bash 4.x. So the last thing works great. Can you explain to me what does the '< <' mean? Or what is it called so I can read about it? I see you had to run the egrep in a subshell instead of say $(...)? Is there a reason for this? Thanks.
It's not < < that means anything. < means a thing, and <() means another thing, and they can be used together. :)
...and by the way, $(...) is also a subshell. <() is process substitution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.