Greyson Gundrum
Greyson Gundrum

Reputation: 1

Containing an output of Awk into a variable and comparing it against an integer

I've spent the last 90 minutes Googling this problem so I hope I am not asking a question that has been asked several times before.8

I am currently trying to parse a log file and need a specific value from a line. Text file contents:

read: 4163419415       0      0     4163419415   0   4395.007      0
read: 4163419415       0      0     4163419415   0   4395.007      0
read: 4163419415       0      0     4163419415   0   4395.007      1
read: 4163419415       0      0     4163419415   0   4395.007      0

I need to grab that last value (which can be variable) and do some checking against it.

COUNTER=1
while [ $COUNTER -lt 26 ]; do
    ECREAD=$(awk '/read:/{i++}i=='$COUNTER'{print $8; exit}' ./txt.file)
    echo $ECREAD
    if [ "$ECREAD" > 0 ] 
        then
            echo "fail"
    fi
let COUNTER=COUNTER+1
done

My problem is the if statement is always true regardless of what value $ECREAD is. I have tried

[[ $ECREAD =~ ^-?[0-9]+$ ]] && echo integer

to verify that the variable is an integer.

I have also tried removing any white space that may be at the end of the variable by piping the awk portion of the command into sed 's/ *$//' AND sed 's/^ *//' AND tr -d ' '

I have tried changing the [] around the IF statement into [[]] AND (( ))

I have tried changing the > symbol into the -gt symbol.

Upvotes: 0

Views: 1193

Answers (4)

David C. Rankin
David C. Rankin

Reputation: 84551

Here a pure bash solution can be just as easy as parsing the file with awk. read has the ability to read the space, tab or newline delimited text (default: internal field separator $IFS) without changing the delimiter. You can then just read the first seven values into a junk variable and discard, leaving only the ecread value you are interested in.

The awk solution is fine as well, but if you are working in bash, it can fill your need as well without relying on an external process:

#!/bin/bash

## accept filename as first argument to script (default dat/ecread.dat)
fn="${1:-dat/ecread.dat}"

## validate file is readable
[ -r "$fn" ] || {
    printf "error: file not readable '%s'. Usage: %s <filename> (dat/ecread.dat)\n\n" "$fn" "${0//*\//}"
    exit 1
}

declare -i counter=1        # declare counter as int, set to 1

## read each line in data-file and discard first 7 values (while counter < 26)
while [ "$counter" -lt 26 ] && read a a a a a a a ecread || [ -n "$ecread" ]; do

    printf " ecread: %s" "$ecread"

    ## test ecread > 0, if so fail, if not OK
    [ "$ecread" -gt 0 ] && printf " -- fail\n" || printf " -- OK\n"

    counter+=1

done <"$fn"

exit 0

output:

$ bash ecread.sh
 ecread: 0 -- OK
 ecread: 0 -- OK
 ecread: 1 -- fail
 ecread: 0 -- OK

Upvotes: 0

John1024
John1024

Reputation: 113834

Answer for Revised Question

Suppose that we have inputfile. We want to examine only the first 24 lines. If any of those lines start with read: and have a last column that is greater than zero, then we want to print Fail.

If that is the case, then:

$ awk '/^read:/ && $NF > 0 {print "Fail";} NR>=24{exit;}' inputfile
Fail

How it works

  • /^read:/ && $NF > 0 {print "Fail";}

    For any row that starts with read: and whose last column, $NF, is greater than zero, then Fail is printed.

  • NR>=24{exit;}

    awk makes available a record (line) counter called NR. After line number 24 has been processed, the program will exit without reading further.

Alternative using a bash loop

If the goal is to use awk one line at a time inside of a bash loop, then try:

for ((counter=1; counter<26; counter++))
do
    ecread=$(awk -v num=$counter '/read:/ && NR==num {print $8} NR==num{exit;}' ./txt.file)
    echo ecread=$ecread
    if [ "$ecread" ] && [ "$ecread" -gt 0 ]
    then
        echo "fail"
    fi
done

How it works

  • for ((counter=1; counter<26; counter++)); do

    This starts a bash loop over counter.

  • ecread=$(awk -v num=$counter '/read:/ && NR==num {print $8} NR==num{exit;}' ./txt.file)

    This passes in the value of the bash variable counter into awk as the variable num. If line number num contains read:, then the value of field 8 is printed. After processing line number num, awk exits.

  • if [ "$ecread" ] && [ "$ecread" -gt 0 ]

    This starts an if-then statement. The then part is executed if (a) ecread has a non-empty value and (b) if that non-empty value is greater than zero.

Answer for Original Question

Try:

ECREAD=$(awk '/read :/{print $8; exit}' ./txt.file)
echo ECREAD=$ECREAD
if [ 1 = "$(echo "$ECREAD > 0" | bc)" ]
then
    echo "fail"
fi

Notes

  • /read:/ does not match anything. The input, at least as displayed in the question, has a space between read and :. Thus, the test is changed to /read :/

  • The purpose of the code {i++}o==$COUNTER' was not clear and it was removed.

  • The value of ECREAD in the sample input is a float: 4395.007. The shell only does integer arithmetic. However, bc handles floats well. So the test was converted to use bc.

  • When doing comparisons with the shell test command, [, The symbols > and < compare string values lexicographically. For numeric tests, one can use the mnemonically-named -eq, -ne, -lt, -le, -gt, or -ge.

The output from the above code on the provided sample input is:

ECREAD=4395.007
fail

Alternative Interpretation

On the chance that there is a misplaced space in the sample input, consider this alternative. Assume that the input looks like:

read: 4163419415 0 0 4163419415 0 4395.007 0

Now, the 8th field is not a float but a zero. In this case, bc is not needed and the code can be simplified to:

ECREAD=$(awk '/read:/{print $8; exit}' ./txt.file)
echo ECREAD=$ECREAD
if [ "$ECREAD" -gt 0 ]
then
    echo "fail"
fi

The output from the above code is:

ECREAD=0

Upvotes: 2

Jonathan Leffler
Jonathan Leffler

Reputation: 753635

The assignment line is a bit of a mess:

ECREAD=$(awk '/read:/{i++}o==$COUNTER'{print $8; exit}' ./txt.file)

There are three single quotes and no backslashes, which makes for trouble. You probably need:

ECREAD=$(awk '/read:/ {i++}  o==$COUNTER {print $8; exit}' ./txt.file)

That's syntactically valid. It isn't clear that it does what you expect. The variable i is incremented if the pattern matches (it doesn't match your sample data) but is otherwise unused. The test o == $COUNTER compares the unset variable o with $0 (because COUNTER is an unset variable, and hence evaluates to 0). Since the conditions is not true, the printing is not executed. This might do what you intended:

ECREAD=$(awk '/^read :/ { print $8; exit}' ./txt.file)

Your shell 'if' statement is a big mess:

[ if "$ECREAD" > 0 ] 
then
echo "fail"
fi

It should be:

if [ "$ECREAD" -gt 0 ] 
then
    echo "fail"
fi

or:

if [[ "$ECREAD" -gt 0 ]] 
then
    echo "fail"
fi

Upvotes: 0

nu11p01n73R
nu11p01n73R

Reputation: 26667

You can try something like

$ ECREAD=$(awk '$1~/read/{print $7}' input )
$ if [ $ECREAD -gt 0 ]
> then 
> echo fail
> else
> echo pass
> fi
pass

Upvotes: 0

Related Questions