Kalib Zen
Kalib Zen

Reputation: 925

How do I print 2 columns with looping from a file in bash

I have the following output.txt it consists only 2 columns to demonstrate:

Test1 Test1-IS-OK
Test2 Test2-IS-NOT
Test3 Test3-IS-OK
Test4 Test4-IS-OK
Test5 Test5-IS-NOT

Then my bash script has the following code:

#!/bin/bash
output="output.txt"
a=$(awk '{ print $1 }' $output)
b=$(awk '{ print $2 }' $output)

while IFS=" " read -r $a $b
do
    echo "LOG: $a and $b"
done < "$output"

I got the following error:

./test.sh: line 13: read: `Test1-IS-OK': not a valid identifier

I need to have output like this

LOG: Test1 and Test1-IS-OK
LOG: Test2 and Test2-IS-NOT
LOG: Test3 and Test3-IS-OK
LOG: Test4 and Test4-IS-OK
LOG: Test5 and Test5-IS-NOT

But the code is not working. What is the best method to loop this 2 columns from a file? Is there a simpler method?

Upvotes: 4

Views: 3149

Answers (4)

Hilton Fernandes
Hilton Fernandes

Reputation: 660

Please consider transferring the awk parsing to the loop, where it belongs:

#!/bin/bash

output="output.txt"

while read -r line 
do
    a=$(echo "${line}" | awk '{print $1}')
    b=$(echo "${line}" | awk '{print $2}')
    echo "LOG: $a and $b"
done < "$output"

Edited according to a good suggestion by @EdMorton

Upvotes: 3

Timur Shtatland
Timur Shtatland

Reputation: 12465

Use this Perl one-liner:

perl -lane 'print "LOG: $F[0] and $F[1]";' output.txt > new.txt

The Perl one-liner uses these command line flags:
-e : Tells Perl to look for code in-line, instead of in a file.
-n : Loop over the input one line at a time, assigning it to $_ by default.
-l : Strip the input line separator ("\n" on *NIX by default) before executing the code in-line, and append it when printing.
-a : Split $_ into array @F on whitespace or on the regex specified in -F option.

SEE ALSO:
perldoc perlrun: how to execute the Perl interpreter: command line switches

Upvotes: 1

Walter A
Walter A

Reputation: 20032

What are the problems with your code?

a=$(awk '{ print $1 }' $output)

With echo "a=${a}" you will see, that a is filled with the output for all lines. You were trying to make some find of function, to be called after $a.

while IFS=" " read -r $a $b

Now you are trying to call the "functions" a and b. The code will substitue the value of the variables before reading the inputfile. when a is filled with "Test1 Test2" the code will try to fill the fields $Test1 and $Test2.

When you only want to change the output, without passing the variables to another statement, you can use awk, or

sed -E 's/([^ ]*) ([^ ]*).*/LOG: \1 and \2/' $output
# or
printf 'LOG: %s and %s\n' $(<$output)

In your case, you can make read reading two parameters:

while read -r a b 
do
    echo "LOG: $a and $b"
done < "$output"

Upvotes: 2

anubhava
anubhava

Reputation: 786289

Best is to avoid bash and do this completely in awk. Within awk it is as simple as:

awk '{print "LOG:", $1, "and", $2}' file
LOG: Test1 and Test1-IS-OK
LOG: Test2 and Test2-IS-NOT
LOG: Test3 and Test3-IS-OK
LOG: Test4 and Test4-IS-OK
LOG: Test5 and Test5-IS-NOT

Upvotes: 7

Related Questions