Byron Hawkins
Byron Hawkins

Reputation: 2695

Bash: read lines into an array *without* touching IFS

I'm trying to read the lines of output from a subshell into an array, and I'm not willing to set IFS because it's global. I don't want one part of the script to affect the following parts, because that's poor practice and I refuse to do it. Reverting IFS after the command is not an option because it's too much trouble to keep the reversion in the right place after editing the script. How can I explain to bash that I want each array element to contain an entire line, without having to set any global variables that will destroy future commands?

Here's an example showing the unwanted stickiness of IFS:

lines=($(egrep "^-o"  speccmds.cmd))
echo "${#lines[@]} lines without IFS"

IFS=$'\r\n' lines=($(egrep "^-o" speccmds.cmd))
echo "${#lines[@]} lines with IFS"

lines=($(egrep "^-o"  speccmds.cmd))
echo "${#lines[@]} lines without IFS?"

The output is:

42 lines without IFS
6 lines with IFS
6 lines without IFS?

Upvotes: 3

Views: 2717

Answers (2)

glenn jackman
glenn jackman

Reputation: 246774

Are you trying to store the lines of the output in an array, or the words of each line?

  1. lines

    mapfile -t arrayname < <(your subshell)
    

    This does not use IFS at all.

  2. words

    (your subshell) | while IFS=: read -ra words; do ...
    

    The form var=value command args... puts the var variable into the environment of the command, and does not affect the current shell's environment.

Upvotes: 7

Charles Duffy
Charles Duffy

Reputation: 295353

This question is probably based on a misconception.

IFS=foo read does not change IFS outside of the read operation itself.

Thus, this would have side effects, and should be avoided:

IFS=
declare -a array
while read -r; do
  array+=( "$REPLY" )
done < <(your-subshell-here)

...but this is perfectly side-effect free:

declare -a array
while IFS= read -r; do
  array+=( "$REPLY" )
done < <(your-subshell-here)

With bash 4.0 or newer, there's also the option of readarray or mapfile (synonyms for the same operation):

mapfile -t array < <(your-subshell-here)

In examples later added to your answer, you have code along the lines of:

lines=($(egrep "^-o"  speccmds.cmd))

The better way to write this is:

mapfile -t lines < <(egrep "^-o"  speccmds.cmd)

Upvotes: 12

Related Questions