45

Let's say I have a text file 'demo.txt' who has a table in it like this:

1 2 3    
4 5 6    
7 8 9    

Now, I want to read each line separately using the 'readarray' command in bash, so I write:

readarray myarray < demo.txt   

The problem is that it doesn't work. If I try to print 'myarray' with:

echo $myarray

I get:

1 2 3

Also, if I write:

echo ${myarray[1]}

I get:

4 5 6

Instead of:

2

as I expected. Why is that? How can accesses each line separately and in that line get access to each member?

6 Answers 6

43

This is the expected behavior. readarray will create an array where each element of the array is a line in the input.

If you want to see the whole array you need to use

echo "${myarray[@]}"

as echo "$myarray will only output myarray[0], and ${myarray[1]} is the second line of the data.

What you are looking for is a two-dimensional array. See for instance this.

If you want an array with the content of the first line, you can do like this:

$ read -a arr < demo.txt 
$ echo ${arr[0]}
1
$ echo ${arr[1]}
2
$ echo ${arr[2]}
3
Sign up to request clarification or add additional context in comments.

2 Comments

Hi Damien, First, thanks for the quick reply. Second, I can see what you're saying buy I can't see how will I be able to read it the way I want to read it? How can just get an array with the members from the first line:1, 2, 3?
Hi Damien, again, thanks for the reply. I tried your way of using: read -a arr < demo.txt, and it worked but as you said, it only gives me the members of the first line. The question is how to get the rest of the lines without erasing the line before that?
31
readarray rows < demo.txt                                           

for row in "${rows[@]}";do                                                      
  row_array=(${row})                                                            
  first=${row_array[0]}                                                         
  echo ${first}                                                                 
done 

Comments

5

To expand on Damien's answer (and because I can't submit comments yet...) you just iterate on read. What I mean is something like the following

exec 5<demo.txt
for i in `seq 1 ${numOfLinesInFile}`
do
read -a arr -u 5
  for j in `seq 0 ${numOfColumnsMinus1}`
  do
  echo ${arr[$j]}
  done
done

I hope you found a solution already (sorry to bump...). I stumbled upon this page while helping teach a friend and figured others may do the same.

1 Comment

Thanks for the reference on how to do this when one's bash version doesn't have "readarray".
3

How can accesses each line separately and in that line get access to each member?

Per the Bash Reference Manual, Bash provides one-dimensional indexed and associative array variables. So you cannot expect matrix[1][2] or similar to work. However, you can emulate matrix access using a bash associative arrays, where the key denotes a multiple dimension.

For example, matrix[1,2] uses the string "1,2" as the associative array key denoting the 1st row, 2nd column. Combining this with readarray:

typeset -A matrix
function load() {
    declare -a a=( $2 )
    for (( c=0; c < ${#a[@]}; c++ ))
    do
        matrix[$1,$c]=${a[$c]}
    done
}
readarray -C load -c 1 <<< $'1 2 3\n4 5 6\n7 8 9'
declare -p matrix

2 Comments

I fail to see the meaningfulness of readarray here; by default, the command fills the array MAPFILE. In this case, it is just filled with the content of the Here String and the command simply acts as iterator over it - the actual 'matrix' array assignment happens in the repeated invocation of load. To me, it would seem to be much more straightforward, to use a while or for loop here, maybe also better performing, e. g.: readarray -t -d "$IFS" a < demo.txt; declare -i c=0; for ((i=0; i<${#a[@]}; i++)); do matrix["$i,$c"]="${a[$i]}"; done
Alternatively: local -i i=0 c=0; while read -r -d "$IFS"; do matrix["$((i++)),$c"]=$REPLY; done < demo.txt ... I didn't check it with the file operator, but it should work just like your example, with a Here String
0

Sorry to bump but I believe there's an easy and very clean solution for your request:

$ cat demo.txt
1 2 3
4 5 6
7 8 9
$ while read line;do IFS=' ' myarray+=(${line}); done < demo.txt
$ declare -p myarray
declare -a myarray='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9")'
$

Comments

-2

Since 3 out of 5 answers ignore the OPs request to use readarray I'm guessing no one will downvote me for adding another that also fails to use readarray.

Paste the following code unaltered into an Ubuntu bash command line (not tried in any other environment)

Code

# Create test array
echo -e "00 01 02 03 04
10 11 12 13 14
20 21 22 23 24
30 31 32 33 34" > original.txt;

# Reformat test array as a declared bash variable.
sed 's/^/"/g;  s/$/"/g;  1s/^/declare my2d=(\n/;  $s/$/\n);/' original.txt > original.sh;

# Source the bash variable.
source original.sh;

# Get a row.
declare row2=(${my2d[2]});

# Get a cell.
declare cell3=${row2[3]};
echo -e "Cell [2, 3] holds [${cell3}]";

Output

Cell [2, 3] holds [23]

Explanation

The four sed groups do the following:

  1. s/^/"/g; - prepend double quotes to each line
  2. s/$/"/g; - append double quotes to each line
  3. 1s/^/declare my2d=(\n/; - prepend declare my2d=( to file
  4. $s/$/\n); - append );to file

Note

This gets too messy to be worth using if your array elements have whitespace in them

Comments

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.