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?
set -f; arr=($string); set +f
seems to be faster than read -r -a <<< $string
.
shellcheck
SC2206: Quote to prevent word splitting/globbing, or split robustly with mapfile
or read -a
.
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
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.
arr=($line)
in the accepted answer suffers from globbing issues. For example, try: line="twinkling *"; arr=($line); declare -p arr
.
<<<
but it may be a good idea to still use double quotes for consistency and readability.
Try this:
arr=(`echo ${line}`);
line='*'
GNU Make 4.2.1
solution, but it doesn't did the job
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.
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
GNU Make 4.2.1
solution, but is not that
line="1 1.50 string"
arr=$( $line | tr " " "\n")
for x in $arr
do
echo "> [$x]"
done
tr
is superfluous but it should loop over "${arr[@]}"
instead, not $arr
Success story sharing
for i in ${arr[@]}; do echo $i; done
echo ${arr[@]}
$line
has globbing characters in it.mkdir x && cd x && touch A B C && line="*" arr=($line); echo ${#arr[@]}
gives 3declare -a "arr=($line)"
will ignoreIFS
delimiters inside quoted stringsline='*'
,read -a arr <<<$line
always work, but onlyarr=($line)
fails.