Reading an array from a file in bash - “not found”

2019-09-20 00:34发布

问题:

I have a text file with a few basic words:

-banana
-mango
-sleep

When I run my script:

#!/bin/sh
WORD_FILE="testwoord.txt"
WORDS_ARRAY=cat $WORD_FILE

The output is like:

/home/username/bin/testword.txt: 1 /home/username/bin/restword.txt: banana: not found
/home/username/bin/testword.txt: 1 /home/username/bin/restword.txt: mango: not found
/home/username/bin/testword.txt: 1 /home/username/bin/restword.txt: sleep: not found

Why is it doing this? What I actually want is a script that reads words from a .txt file and puts it in an array.

回答1:

To explain why this doesn't work:

WORDS_ARRAY=cat $WORD_FILE

runs the command generated by expanding, string-splitting, and glob-expanding $WORD_FILE with the variable WORDS_ARRAY exported in the environment with the value cat.


Instead, consider:

#!/bin/bash
#      ^^ -- important that this is bash, not sh: POSIX sh doesn't have arrays!

WORD_FILE=testword.txt
readarray -t WORDS_ARRAY <"$WORD_FILE"
printf 'Read a word: %q\n' "${WORDS_ARRAY[@]}"

...which will create an actual array, not a string variable containing whitespace (as WORDS_ARRAY=$(cat $WORD_FILE) would).


By the way, using all-upper-case variable names is bad form here. To quote the POSIX spec:

Environment variable names used by the utilities in the Shell and Utilities volume of POSIX.1-2008 consist solely of uppercase letters, digits, and the ( '_' ) from the characters defined in Portable Character Set and do not begin with a digit. Other characters may be permitted by an implementation; applications shall tolerate the presence of such names. Uppercase and lowercase letters shall retain their unique identities and shall not be folded together. The name space of environment variable names containing lowercase letters is reserved for applications. Applications can define any environment variables with names from this name space without modifying the behavior of the standard utilities.



回答2:

To complement Charles Duffy's helpful answer:

Note that the variable names were changed to lowercase, as per Charles' recommendation.

Here a bash 3.x (and above) version of the command for reading lines into a bash array (readarray requires bash 4.x):

IFS=$'\n' read -d '' -ra words_array < "$word_file"

If you want to store individual words (across lines), use:

read -d '' -ra words_array < "$word_file"

To print the resulting array:

for ((i=0; i<"${#words_array[@]}"; i++)); do echo "word #$i: "${words_array[i]}""; done