Reputation: 75649
Consider the following script:
#!/bin/bash
while true; do
IFS=';' read -r
echo "Read: $REPLY"
done
If I run this script and type in 1;2;3
, I expect and desire to see the output:
Read: 1
Read: 2
Read: 3
However, the output I get is
Read: 1;2;3
It seems like IFS is being ignored.
What is happening here, and is it possible to get the desired output?
Note that I am already aware of the array option (read -a
and then loop through the array), and I am specifically interested in the single read case.
Upvotes: 0
Views: 172
Reputation: 35516
Assuming you will always have the same number (3 in this case) of delimited fields:
while true
do
IFS=';' read -r -p "enter string: " a b c
echo "#############"
echo "Read: ${a}"
echo "Read: ${b}"
echo "Read: ${c}"
echo "#############"
done
NOTE: if there are more than 3 delimited fields c
will be populated with fields 3-n with the delimiter included between the fields
Taking for a test drive:
enter string: 1;2;3
#############
Read: 1
Read: 2
Read: 3
#############
enter string: 1;2;3;4
#############
Read: 1
Read: 2
Read: 3;4 # 3rd/4th fields stored in c
#############
enter string: a b c;d e f;g h i
#############
Read: a b c
Read: d e f
Read: g h i
#############
enter string:
If you don't know in advance the number of fields we can modify the read
to parse and store the fields into an array then loop over the array:
while true
do
IFS=';' read -r -p "enter string: " -a arr # split input on delimiter ; and place results in array arr[]
# typeset -p arr # uncomment to display the contents of the array
echo "#############"
for ((i=0; i<${#arr[@]}; i++))
do
echo "Read: ${arr[i]}"
done
echo "#############"
done
Taking for a test drive (with the typeset -p
uncommented):
enter string: 1;2;3
declare -a arr=([0]="1" [1]="2" [2]="3")
#############
Read: 1
Read: 2
Read: 3
#############
enter string: 1;2;3;4
declare -a arr=([0]="1" [1]="2" [2]="3" [3]="4")
#############
Read: 1
Read: 2
Read: 3
Read: 4 # 4th field stored in a separate array location
#############
enter string: a b c;d e f;g h i
declare -a arr=([0]="a b c" [1]="d e f" [2]="g h i")
#############
Read: a b c
Read: d e f
Read: g h i
#############
enter string:
Upvotes: 0
Reputation: 532508
read
reads one line at a time. The value of IFS
controls word splitting, so that if you wrote
IFS=';' read -r a b c
then your line would be split on ;
into at most 3 fields. You could then print each field separately.
IFS=';' read -r a b c <<< "1;2;3"
echo "Read: $a"
echo "Read: $b"
echo "Read: $c"
If you don't know how many fields there will be, you can populate an array by splitting as many times as possible and collecting the results. You can then iterate over the array.
IFS=';' read -ra arr <<< "1;2;3"
for x in "${arr[@]}"; do
echo "Read: $x"
done
Upvotes: 2