Home > Net >  Simple bash program which compares values
Simple bash program which compares values

Time:11-15

I have a file which contains varoius data (date,time,speed, distance from the front, distance from the back), the file looks like this, just with more rows:

2003.09.23.,05:05:21:64,134,177,101
2009.03.10.,17:46:17:81,57,102,57
2018.01.05.,00:30:37:04,354,145,156
2011.07.11.,23:21:53:43,310,125,47
2011.06.26.,07:42:10:30,383,180,171

I'm trying to write a simple Bash program, which tells the dates and times when the 'distance from the front' is less than the provided parameter ($1) So far I wrote:

#!/bin/bash
if [ $# -eq 0 -o $# -gt 1 ]
then
    echo "wrong number of parameters"
fi
i=0
fdistance=()
input='auto.txt'
while IFS= read -r line
do
    year=${line::4}
    month=${line:5:2}
    day=${line:8:2}
    hour=${line:12:2}
    min=${line:15:2}
    sec=${line:18:2}
    hthsec=${line:21:2}
    fdistance=$(cut -d, -f 4)
    if [ "$fdistance[$i]" -lt "$1" ]
    then
        echo "$year[$i]:$month[$i]:$day[$i],$hour[$i]:$min[$i]:$sec[$i]:$hthsec[$i]"
    fi
    i=`expr $i   1`
done < "$input"

but this gives the error "whole expression required" and doesn't work at all.

CodePudding user response:

Would you try the following:

#/bin/bash

if (( $# != 1 )); then
    echo "usage: $0 max_distance_from_the_front" >& 2           # output error message to the stderr
    exit 1
fi

input="auto.txt"

while IFS=, read -r mydate mytime speed fdist bdist; do         # split csv and assign variables
    mydate=$(tr '.' ':' <<< ${mydate%.})                        # reformat the date string
    if (( fdist < $1 )); then                                   # if the front disatce is less than $1
        echo "$mydate,$mytime"                                  # then print the date and time
    fi
done < "$input"

Sample output with the same parameter as Keldorn:

$ ./test.sh 130
2009:03:10,17:46:17:81
2011:07:11,23:21:53:43

CodePudding user response:

There are a few odd things in your script:

  • Why is fdistance an array. It is not necessary (and here done wrong) since the file is read line by line.
  • What is the cut of the line fdistance=$(cut -d, -f 4) supposed to cut, what's the input?
  • (Note: When invalid parameters, better end the script right away. Added in the example below.)

Here is a working version (apart from the parsing of the date, but that is not what your question was about so I skipped it):

#!/usr/bin/env bash

if [ $# -eq 0 -o $# -gt 1 ]
then
    echo "wrong number of parameters"
    exit 1
fi

input='auto.txt'
while IFS= read -r line
do
    fdistance=$(echo "$line" | awk '{split($0,a,","); print a[4]}')
    if [ "$fdistance" -lt "$1" ]
    then
        echo $line
    fi
done < "$input"

Sample output:

$ ./test.sh 130
2009.03.10.,17:46:17:81,57,102,57
2011.07.11.,23:21:53:43,310,125,47
$ 

CodePudding user response:

If you have the option of using awk, the entire process can be reduced to:

awk -F, -v dist=150 '$4<dist {split($1,d,"."); print d[1]":"d[2]":"d[3]","$2}' file

Where in the example above, any record with distance (field 4, $4) less than the dist variable value takes the date field (field 1, $1) and splits() the field into the array d on "." where the first 3 elements will be year, mo, day and then simply prints the output of those three elements separated by ":" (which eliminates the stray "." at the end of the field). The time (field 2, $2) is output unchanged.

Example Use/Output

With your sample data in file, you can do:

$ awk -F, -v dist=150 '$4<dist {split($1,d,"."); print d[1]":"d[2]":"d[3]","$2}' file
2009:03:10,17:46:17:81
2018:01:05,00:30:37:04
2011:07:11,23:21:53:43

Which provides the records in the requested format where the distance is less than 150. If you call awk from within your script you can pass the 150 in from the 1st argument to your script.

You can also accomplish this task by substituting a ':' for each '.' in the first field with gsub() and outputting a substring of the first field with substr() that drops the last character, e.g.

awk -F, -v dist=150 '$4<dist {gsub(/[.]/,":",$1); print substr($1,0,length($1)-1),$2}' file

(same output)

While parsing the data is a great exercise for leaning string handling in shell or bash, in practice awk will be Orders of Magnitude faster than a shell script. Processing a million line file -- the difference in runtime can be seconds with awk compared to minutes (or hours) with a shell script.

If this is an exercise to learn string handling in your shell, just put this in your hip pocket for later understanding that awk is the real Swiss Army-Knife for text processing. (well worth the effort to learn)

  • Related