Reading a string and passing passing arguments to a while loop

I have an for loop that reads the following file

cat param.cfg
val1:env1:opt1
val2:env2:opt2
val3:env3:opt3
val4:env4:opt4
.
.

The for loop extracts the each line of the file so that at any one point, the value of i is

val1:env1:opt1

etc...

I would like to extract each individual attribute and pass it to a while loop as follows:

for i in $( cat refire.cfg | tail +$START | head -$STOP | grep -v "^#" | grep ':' )
do
# read the value of i here and break it down as arguments x y z for this loop
while read x y z 
do
stuff
done
done

I want to be able to repeat the while loop for each iteration of i

If I got it right, you can break it down altering IFS:

OLDIFS=$IFS
IFS=":"

while read A B C; do
   do someting here with $A or $B or $C
done < infile

IFS=$OLDIFS
1 Like

Thank you Zaxxon.

I understand from your code what you're trying to read the i/p file in the while loop. I actually want to read the value of the variable i from the for loop.

When i try to make the while loop from $i , i am getting the following error :

Any thoughts?

---------- Post updated at 03:49 PM ---------- Previous update was at 03:27 PM ----------

Is there any way that the value of the variable i be stored in a file or a variable that can be read by the while loop.

I have tried writing to a file and it does not work.

for i in $( cat rparam.cfg | tail +$RESPONSE | head -$RESPONSESTOP | grep -v "^#" | grep ':' )
 
echo $i
$i > .tempparam
while read x y z
do
<stuff>
done < tempparam
rm -f tempparam
done

Maybe you post a snippet of that refire.cfg or now called rparam.cfg so we can provide some more direct code. The grep'ing might not be needed to supply a for loop when being used directly on that file.

Here you go. To be honest, i dont even need the while loop above as long as the arguments x, y and z can be extracted from the variable i

RED:value1:Y
RED:value2:Y
#RED:value3:Y
BLUE:value4:Y
RED:value5:Y
RED:value6:Y
BLUE:value7:Y
BLUE:value8:Y
RED:value9:Y
RED:value10:Y
RED:value11:y

In your original example the variable i was assigned the whole record (e.g. val1:env1:opt1 ). In the code that zaxxon suggested, the components val1 env1 opt1 were broken out and assigned to variables A, B and C. There would be no need to reference i, as there wasn't anything assigned to i anyway. You should be able to use A, B, and C as is.

From your original post, you also do a lot of extra work to only read a subset of lines from the input file. That isn't handled by zaxxon's example. Moving along the same lines as was offered, the following code will generate just the lines between START and STOP (inclusive) and assign them to the variables f1, f2, and f3 (A, B, C).

awk -F : -v last=${STOP:-0} -v first=${START:-0} '
    /^#/ { next; }
    NR >= first && (last == 0  || NR <= last) {
        print $1, $2, $3, $0
    }

' input-file | while read f1 f2 f3 i
do
        echo "first field: $f1"
        echo "second field $f2"
        echo "third field $f3"
        echo "whole record (i) $i"
done
1 Like

Thanks Agama. Having tried the code above, i am getting an EOF error. Can you please explain what the awk is doing and if the format of the code is correct? I am getting the following error

---------- Post updated at 06:48 PM ---------- Previous update was at 05:36 PM ----------

Hi Agama,

I am unfamiliar with the syntax of the belwo code that you provided. I am getting an error from the code. Can you please let me know if the format of the code is correct? Thanks in advance.

awk -F : -v last=${STOP:-0} -v first=${START:-0} '
    /^#/ { next; }
    NR >= first && (last == 0  || NR <= last) {
        print $1, $2, $3, $0
    }
 
' input-file | while read f1 f2 f3 i
do

I cut/pasted the code in your post and it seems fine to me; I'm not getting an error. Can you past in your whole script, there is likely a typo somewhere in it.

An explanation:

The STOP and START shell variables allow you to define a line range from the input file. If not defined, the whole file is processed. If only START is defined, all lines before START are skipped. If STOP is defined, all lines after that line are skipped.

awk -F : -v last=${STOP:-0} -v first=${START:-0} '
    /^#/ { next; }   # if a line in the input starts with a hash, it is skipped

    # this section prints the first three fields and the whole line 
    # the $0 prints the whole line with the separators and is needed only to 
    # show that as $1 in the shell script echo
    NR >= first && (last == 0  || NR <= last) {
        print $1, $2, $3, $0
    }
' input-file
1 Like

Thanks a mil for the explanation. It's gonna be pretty handy.
I use the same code that you've posted and get the EOF error. My frustrations not withstanding, i have gone ahead and written the results of the for loop into a tempfile and then proceeded to read the code from below(i have the working section commented out). I'd still like to understand why the below code throws up an error. I am only new to shell script and i'd like to understand whats going on so as not to repeat the errors. Thank you for your time

for i in $( cat param.txt | tail +$START | head -$STOP | grep -v "^#" | grep ':' )
                             do
                                   #echo $i
                                  # printf "%s\n" "$i" >> .param
                                   #printf "Press [enter] key to continue. . ."
                                   #read enterKey
                                   OLDIFS=$IFS
                                   IFS=":"
                                awk -F : -v last=${RESPONSESTOP:-0} -v first=${RESPONSE:-0} '
                                   /^#/ { next; }
                                   NR >= first && (last == 0  || NR <= last) {
                                    print $1, $2, $3, $0
                                    }
                                   ' param.txt | while read x y z i
                                   do
                                        echo "first field $x"
                                        echo "second field $y"
                                        echo "third field $z"
                                        echo "whole record (i) $i
                                   done
                                   #while read x y z
                                   #do
                                   #if [ "$z" == "GOD" ] ; then
                                   #       function1 $x $y
                                   #       function2 $y
                                   #       function3 $y
                                   #fi
                                   #if [ "$z" == "DEVIL" ] ; then
                                   #       function4
                                #   fi
                                #   done < .param
                                   IFS=$OLDIFS
                             done
                      #  echo "`cat .param`"
                        #rm -rf .param

You had a missing quote on one of the echo statements.

I think this is all that you need. The cat.... statement isn't necessary. Awk reads the param.txt file and thus you don't need to cat it through head/tail to get it into the awk.

awk -F : -v last=${RESPONSESTOP:-0} -v first=${RESPONSE:-0} '
   /^#/ { next; } 

   NR >= first && (last == 0  || NR <= last) { 
        print $1, $2, $3, $0
    }
   ' param.txt | while read x y z i 
   do
        echo "first field $x"
        echo "second field $y"
        echo "third field $z"
        echo "whole record (i) $i"   # <missing close quote was here

        if [[ $z == "GOD" ]]
        then
                function1 $x $y
                function2 $y
                function3 $y
        fi
        if [[ $z == "DEVIL" ]]
        then
            function4
        fi
   done

Or alternatively, use plain shell without pipes, e.g.

START=2; STOP=7
linenr=0
while IFS=: read x y z ; do
  linenr=$((linenr+1));
  [ $linenr -ge $START ] || continue
  [ $linenr -le $STOP  ] || break
  case $x in 
    \#*) continue
  esac
  echo " Do your stufff with $x $y and $z at $linenr" 
done < infile
 Do your stufff with RED value2 and Y at 2
 Do your stufff with BLUE value4 and Y at 4
 Do your stufff with RED value5 and Y at 5
 Do your stufff with RED value6 and Y at 6
 Do your stufff with BLUE value7 and Y at 7
1 Like

Hi agama,

I have rectified the missing quote. when i run the script, i get the following error

---------- Post updated at 07:40 AM ---------- Previous update was at 07:26 AM ----------

In the above code, if i change the values of start and stop to 7 and 18 respectively and try to execute

echo " Do your stufff with $x $y and $z at $linenr" 

i am not getting any output. I can only print the values between 1 and 6. If i try to print from 6 onwards, there is no output (I have over 50 files btw).

Hi, I just tried it and it works here. Could you post a representative sample of say 25 lines of the file you tested with? What shell/OS are you using?

I use a bash shell in Solaris.

I am using a file with the format given below. If i choose any value from 1-5 as start and end at say 15, your logic works. But if i choose say 7-13 , then the

  • dows not happen.
RED:value1:Y
RED:value2:Y
RED:value3:Y
BLUE:value4:Y
RED:value5:Y
RED:value6:Y
BLUE:value7:Y
BLUE:value8:Y
RED:value9:Y
RED:value10:Y
RED:value11:y
BLUE:value12:Y
BLUE:value13:Y
RED:value14:Y
BLUE:value15:Y
RED:value16:y

Hi, what does echo $SHELL give? What shebang are you using in your script? On Solaris /bin/sh is not a POSIX shell, you should use /usr/xpg4/bin/sh instead or use bash.

echo $SHELL gives me

But my shebang is

Could you post a copy of the script exactly like you are using it?

 
function function1{
...
}
function 2{
...
}
for i in $( cat param.cfg | grep -v "^#" | grep ':' )
                             do
                                printf "$n. $i \t"
                                n=`expr $n + 1`
                             done
                             echo Please enter START     
                            read START
                            echo Please enter STOP
 read STOP
STOP=`expr $STOP - $START + 1`
n=$START
                             for i in $( cat rparam.cfg | tail +$START | head -$STOP | grep -v "^#" | grep ':' )
                             do
                                     echo "$n. $i"
                                     n=`expr $n + 1`
                             done
OLDIFS=$IFS
                                   IFS=":"
linenr=0
                                while IFS=: read x y z ; do
                                  linenr=$((linenr+1));
                                  [ $linenr -ge $RESPONSE ] || continue
                                  [ $linenr -le $RESPONSESTOP  ] || break
                                  case $x in
                                    \#*) continue
                                  esac
                                  function1
                                done < param.cfg

The functions above are standard and it doesnt interfere with the o/p of the while loop. The main part of the code is as above. I've removed all the formattin echo's thats not relevant to the logic

---------- Post updated at 01:22 PM ---------- Previous update was at 01:16 PM ----------

A sample output of the above shell for different options is:

START=2 STOP=9

RED:value2:Y
RED:value3:Y
BLUE:value4:Y
RED:value5:Y
RED:value6:Y
BLUE:value7:Y
BLUE:value8:Y

START =5 STOP=12

RED:value5:Y
RED:value6:Y
BLUE:value7:Y
BLUE:value8:Y

START =12 STOP=24

RED:value12:Y
RED:value13:Y

START =21 STOP=34

Nothing is displayed

Hi, where do $RESPONSE and $RESPONSESTOP get set?

I am very sorry Scrutinizer. RESPONSE = START; RESPONSESTOP=STOP