6

I am implementing as below code using for loop but wrong output coming after running the script.

for i in `awk -F"|" '{print $1}' $INPUTFILE`, j in `awk -F"|" '{print $2}' $INPUTFILE`
do
echo $i:$j
done  

Please help me to use multiple variables in single for loop in shell script.

10

bash allows for loops with more than one variable, but only in C like syntax:

for ((i=0,j=10;i<=j;i++,j--))
do
   echo "i=$i"
   echo "j=$j"
done
  • how are i and j read from $INPUTFILE ? – Archemar Apr 28 '16 at 15:43
  • @Archemar They're not. The question, I believe, is wrong. Using for loops on awk is usually wrong as you can usually accomplish whatever you wanted in pure awk, and see Stéphane Chazelas's answer. I just answered the question in the title. – Dani_l Apr 28 '16 at 16:03
8

Maybe you actually want:

while IFS='|' read -r i j rest <&3; do
  {
    printf '%s\n' "something with $i and $j"
  } 3<&-
done 3< "$INPUTFILE"

But using a shell loop to process text is often the wrong way to go.

Here, it sounds like you just need:

awk -F '|' '{print $1 ":" $2}' < "$INPUTFILE"

Now as an answer to the question in the title, for a shell with for loops taking more than one variables, you've got zsh (you seem to already be using zsh syntax by not quoting your variables or not disabling globbing when splitting command substitution):

$ for i j in {1..6}; do echo $i:$j; done
1:2
3:4
5:6

Or the shorter form:

for i j ({1..6}) echo $i:$j

The equivalent with POSIX shells:

set -- 1 2 3 4 5 6
## or:
# IFS='
# ' # split on newline
# set -f # disable globbing
# set -- $(awk ...) # split the output of awk or other command
while [ "$#" -gt 0 ]; do
  echo "$1:$2"
  shift 2
done
  • Is there any way to do it without using a new fd (<&3) and what does - after that do? Asking out of curiosity. – sdkks Aug 3 '17 at 0:57
  • @sdkks, you can use any fd you like (between 0 and 9, some shells allowing more, some even allowing allocating them dynamically), but it's better to avoid 0, 1 and 2 as those are special. That's stdin, stdout and stderr and the commands inside the loop may need to use them (in the case of printf only 1 and possibly 2 for errors). 3<&- is the syntax to close a fd. We're closing 3 for the commands inside the loop as they don't need access to (and shouldn't use) that resource. – Stéphane Chazelas Aug 3 '17 at 8:25
0

You'll need multiple loops if you want to iterate over more than one variable, like for i in 1 2 3; do for j in a b c; do ...; done; done. when the list of things to iterate over is the output of a another command, you'll need to wrap that other command in $().

  • I am not sure OP want a cross product, more likely OP want i,j from first line of $INPUTFILE, then i,j from second line and so on. – Archemar Apr 28 '16 at 15:42
  • I opted for not trying to parse the awk parts of the question, but after re-reading it, I think you're right an that Stephane's answer is right on. – Henrik Apr 28 '16 at 17:10
0

You can store an output of a file in a variable and then break it into two. example:

in example file contents are:

a:1
b:2
c:3
d:4

now script.

#!/bin/bash
for n in $(cat example)
do
    first=$(cat $n | cut -d ":" -f 1)
    second=$(cat $n | cut -d ":" -f 2)
    echo $first
    echo $second
done
0

On embedded systems, you are often crippled on what is available. EG. for can only have one variable; awk & printf are simply not available; or IFS is not supported; etc.

In these circumstances you can achieve your goal like this:

export i=xxx
for j in `cat INPUTFILE` ; do \
    [ $i == xxx ] && export i=$j && continue ; \
    echo "$i:$j" ; \
    export i=xxx ; \
done
unset i

A more general form of this solution might look like:

export A=xxx
export B=xxx
for i in 1 2 3 4 5 6 7 8 9 10 11 ; do \
    [ $A == xxx ] && export A=$i && continue ; \
    [ $B == xxx ] && export B=$i && continue ; \
    echo "$A:$B:$i" ; \
    export A=xxx ; \
    export B=xxx ; \
done
unset A
unset B
  • Assumes the string "xxx" never appears in your data-set ...If it does/might, pick something else.
  • You can have any number of "fields" per "record" ...Just duplicate each of the FOUR lines which handle the fields {A, B, ...}.
  • The last field is always called $i ...Feel free to add an export C=$i just before the echo if it helps.
  • If the number of strings in the data-set is not a multiple of the record-size, the "remainder" (ie. |set| % |record|) will be lost.
  • 1
    I have no idea what you’re saying.  Please explain (1) what you think the question is (i.e., what question you’re answering), (2) what you think is in the input file, and (3) what results you’re trying to produce.  Please do not respond in comments; edit your answer to make it clearer and more complete. – G-Man Mar 27 at 20:49
  • (1) I think the question is the one stated at the top of the page. (2) I think the content of the input file is specified [vaguely implied] by the question. (3) I am successfully producing the functionality required by the [last line of] question ...The information in this comment does not belong in the answer. – BlueChip Mar 29 at 18:12
0

GNU Parallel can often be a solution for this:

parallel -a $INPUTFILE --colsep '|' echo {1}:{2}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.