4

Say I have the file foo.txt

"The" "quick brown" "fox" "jumps over" "the" "lazy dog."

I would like to read these "fields" from the file into an array. However my attempt is failing if the field has a space

$ read -a bar < foo.txt

$ echo ${bar[0]}
"The"

$ echo ${bar[1]}
"quick
3
  • 2
    Do you want to keep the quotation marks? Commented Oct 16, 2012 at 23:51
  • Do you want to keep the spaces as field separator or can you replace them and use a suitable IFS? Commented Oct 16, 2012 at 23:56
  • @Ignacio Vazquez-Abrams: FYI. Commented Oct 17, 2012 at 0:19

4 Answers 4

5

Use declare instead of eval:

declare -a "bar=( $( < foo.txt ) )"

This forces everything in the file to be treated as the right-hand side of the assignment. Using eval, the contents of the file can be interpreted as code. For example, if the file contains

Some text ); echo "you just erased all your files"; ( true

then the following is executed by eval:

bar=( Some text ); echo "you just erased all your files"; ( true )

The parentheses in the file balance the parentheses used outside the command substitution, resulting in the text between them in the file being executed as code rather than being used to populate the array.

Sign up to request clarification or add additional context in comments.

2 Comments

It doesn't work. echo ${bar[0]} : ( "The" "quick brown" "fox" "jumps over" "the" "lazy dog." ). check my solution
Obviously, I spent more time on the counterexample than the original :) The given file works with "-a", but I am having trouble finding a good example and bad example that both work with the same amount of quoting.
1

This works

$ read < foo.txt

$ eval bar=($REPLY)

$ echo ${bar[0]}
The

$ echo ${bar[1]}
quick brown

1 Comment

declare "bar=( $(< tmp.txt))" is a little safer; try the eval if the contents of the file are something like "The" ); echo malicious code; ( true.
1

Your solution fails because bash normally uses whitespaces for input field separation IFS (and that's also why you get the quotes).

You can use plain read, if you prepare your text for processing:

IFS='"'
read -a bar < <(sed 's/"\([^"]*\)" */\1"/g' foo.txt)
echo ${bar[1]}

quick brown

Note: the sed command transforms the file into this format: The"quick brown"fox"jumps over"the"lazy dog.". I use " as delimiter as that's definitely not used in the words.

1 Comment

Only works if your separator is a double-quote
1

Here's one way using GNU awk and the FPAT variable:

IFS=$'\n'
array=($(awk 'BEGIN { FPAT = "([^ ]+)|(\"[^\"]+\")" } { for (i=1; i<=NF; i++) print $i }' file.txt))

echo "${array[1]}"

Result:

"quick brown"

1 Comment

This actually works and is more elegant than other responses. Break (with the field separator) at the newline character and then read the array as whole lines. I've used IFS=$'\n' array=( `whatever_command_that_emits_lines_of_text` ) many times.

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.