1

i have this file

Seq1
10 1 5
10 2 6
10 3 9
Seq2
15 2 7
15 4 9
15 8 12

i want to have arrays for each Seqs (Seq1, Seq2) like this:

2ndColumn=(1,2,3) 
3rdColumn=(5,6,9)

i wrote this but it does not break the while loop..

#!/bin/bash
2ndColumn=()
3rdColumn=()
while read line 
do
if [[ $line == S* ]] 
 echo "$line"
else
 i=0
 while [[ $line != S* ]]
 do
  2ndColumn[i]="$(echo $line | cut -d\  -f2)"
  3rdColumn[i]="$(echo $line | cut -d\  -f3)"
  i=$((i+1))
  read line
 done
 echo "${2ndColumn[@]} and ${3rdColumn[@]}"
fi
done < file
exit 0

this script will iterate forever, it does not get out of while loop. please give a hand to this stupid humanbeing :(

3
  • The reason you're looping forever is because your inner loop never checks whether read succeeded or failed. Commented Dec 1, 2012 at 11:07
  • @Barmar why while does not suffice? Commented Dec 1, 2012 at 12:15
  • When the read fails, line is set to the empty string, and [[ $line != S* ]] is true, so the while-loop keeps looping. The outer loop uses the success of read as its while condition. Commented Dec 1, 2012 at 17:49

3 Answers 3

1

I would restructure it to use a single loop instead of a nested loop with nested read calls on stdin:

2ndColumn=()
3rdColumn=()
i=0
while read line 
do
    if [[ $line == S* ]] 
        echo "$line ==> ${2ndColumn[@]} and ${3rdColumn[@]}"
        # reset the lists...
        2ndColumn=()
        3rdColumn=()
        i=0
    else
        2ndColumn[i]="$(echo $line | cut -d\  -f2)"
        3rdColumn[i]="$(echo $line | cut -d\  -f3)"
        i=$((i+1))
    fi
done

This will avoid issues with the inner read call, which is probably being blocked when fed a stdin file handle.

Sign up to request clarification or add additional context in comments.

Comments

1
  • you can't change the cursor line of a while read

I wrote this for you with a little to prefix the array names with seq N+1 :

#!/bin/bash

file=file.txt

while read line; do
    if [[ $line == S* ]]; then
        echo "$line"
        i=0
        ((Seq++))
    else
        declare seq${Seq}_2ndColumn[i]="$(echo $line | cut -d\  -f2)"
        declare seq${Seq}_3rdColumn[i]="$(echo $line | cut -d\  -f3)"
        ((i++))
    fi
done < "$file"

echo "${!seq*} arrays are declared"

OUTPUT

seq1_2ndColumn seq1_3rdColumn seq2_2ndColumn seq2_3rdColumn arrays are declared

EXPLANATIONS

  • ${!pattern*} is a nice feature of bash to display variables beginning with pattern
  • (( )) is an arithmetic command, which returns an exit status of 0 if the expression is nonzero, or 1 if the expression is zero. Also used as a synonym for "let", if side effects (assignments) are needed. See http://mywiki.wooledge.org/ArithmeticExpression

BONUS

If you have the control on what's in your array, try doing this at the end ::

for s in ${!seq*}; do
    printf '\t%s\n' $(eval echo \${$s[@]})
done

See See http://mywiki.wooledge.org/BashFAQ/048

new OUTPUT

seq1_2ndColumn
        1
        2
        3
seq1_3rdColumn
        5
        6
        9
seq2_2ndColumn
        2
        4
        8
seq2_3rdColumn
        7
        9
        12

Comments

0
file=/PATH/TO/file.txt

arr1=( $(awk '/^Seq/{l++} l==1{print $2}' "$file") )
arr2=( $(awk '/^Seq/{l++} l==1{print $3}' "$file") )

echo "arr1:"
printf '\t%s\n' ${arr1[@]}

echo "arr2:"
printf '\t%s\n' ${arr2[@]}

1 Comment

hey sputnick. as i understand your solution will get all values into array at once, but i have to get the two arrays with respect to header (Seq1,Seq2..) so that i will introduce another calculations..

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.