ChatGPT解决这个技术问题 Extra ChatGPT

Reading a space-delimited string into an array in Bash

I have a variable which contains a space-delimited string:

line="1 1.50 string"

I want to split that string with space as a delimiter and store the result in an array, so that the following:

echo ${arr[0]}
echo ${arr[1]}
echo ${arr[2]}

outputs

1
1.50
string

Somewhere I found a solution which doesn't work:

arr=$(echo ${line})

If I run the echo statements above after this, I get:

1 1.50 string
[empty line]
[empty line]

I also tried

IFS=" "
arr=$(echo ${line})

with the same result. Can someone help, please?

See this answer on Unix & Linux Stack Exchange: Using sed with herestring (<<<) and read -a. set -f; arr=($string); set +f seems to be faster than read -r -a <<< $string.
Related: how to convert a newline-delimited string to a bash array: Convert multiline string to array

G
Gabriel Staples

In order to convert a string into an array, create an array from the string, letting the string get split naturally according to the IFS (Internal Field Separator) variable, which is the space char by default:

arr=($line)

or pass the string to the stdin of the read command using the herestring (<<<) operator:

read -a arr <<< "$line"

For the first example, it is crucial not to use quotes around $line since that is what allows the string to get split into multiple elements.

See also: https://github.com/koalaman/shellcheck/wiki/SC2206


and to do a sanity check of your beautiful new array: for i in ${arr[@]}; do echo $i; done
or just echo ${arr[@]}
Both ways may fail if $line has globbing characters in it. mkdir x && cd x && touch A B C && line="*" arr=($line); echo ${#arr[@]} gives 3
declare -a "arr=($line)" will ignore IFS delimiters inside quoted strings
@Tino No. When line='*', read -a arr <<<$line always work, but only arr=($line) fails.
c
codeforester

In: arr=( $line ). The "split" comes associated with "glob".
Wildcards (*,? and []) will be expanded to matching filenames.

The correct solution is only slightly more complex:

IFS=' ' read -a arr <<< "$line"

No globbing problem; the split character is set in $IFS, variables quoted.


This should be the accepted answer. The statement arr=($line) in the accepted answer suffers from globbing issues. For example, try: line="twinkling *"; arr=($line); declare -p arr.
Quoting is optional for herestring, <<< but it may be a good idea to still use double quotes for consistency and readability.
g
gniourf_gniourf

Try this:

arr=(`echo ${line}`);

Nice -- This solution also works in Z shell where some of the other approaches above fail.
Its does the work, could you please explain why it works?
Remark: this doesn't work either when the line have '*' in it, like line='*'
Looking for a GNU Make 4.2.1 solution, but it doesn't did the job
@artu-hnrq Make use the sh shell. That shell has no arrays. The question is about arrays. Can't give you an answer compatible with both requirements. Unless you claim that the positional arguments are the only array in sh and, then, this: set -- $line; printf '%s\n' "$@" would work. Note that glob characters are still a problem in this case.
D
David Anderson

If you need parameter expansion, then try:

eval "arr=($line)"

For example, take the following code.

line='a b "c d" "*" *'
eval "arr=($line)"
for s in "${arr[@]}"; do 
    echo "$s"
done

If the current directory contained the files a.txt, b.txt and c.txt, then executing the code would produce the following output.

a
b
c d
*
a.txt
b.txt
c.txt

Looking for a GNU Make 4.2.1 solution, but is not that
c
codemania
line="1 1.50 string"

arr=$( $line | tr " " "\n")

for x in $arr
do
echo "> [$x]"
done

The looping is wrong, it splits the array fine and the pipe into tr is superfluous but it should loop over "${arr[@]}" instead, not $arr