AWK and sub/gsub: updating a date/time block

I have a file ("modtest") in which I want to update the last date/time block in the lines beginning with a period. Here is a sample:

.ROMULT 10150908EDT 10270908EDT 10010908EDT   RANGE
RAWV2    1.00
.ROMULT 10150908EDT 10270908EDT 10010908EDT   FGROUP
CHOWANRV 1.00
.RRIMULT 10150908EDT 10270908EDT 10010908EDT
RAWV2    1.13
.RRIMULT 10150908EDT 10270908EDT 10010908EDT   FGROUP
CHOWANRV 1.13
.TSCHNG 10190914EDT  10010908EDT
RAWV2    RAWV2    QINE 6 49.72 48.42 47.11 45.81 44.51 43.20 42.43 &
 41.65 40.88 40.10 39.32 38.55 38.31 38.08 37.85 37.62 37.38 37.15 &
 36.92 36.69 36.55 36.41 36.27 36.13 35.99 35.85 35.71 35.57 &
 PLOT-TUL

These lines have anywhere from 2 to 5 fields. The date/time block I want to update will either be the last or next-to-last field in the line.

awk ' ( NF == 5 ) && ( /^\./ ) {  gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock, $4) ; print } ' modtest

The dateblock variable is mmddyy, two digits for hour, and EST or EDT.

When I run this command, the lines that have 5 fields and begin with a period are changed as follows, but the 4th field that I meant to update does not appear at all:

.ROMULT 10150908EDT 10270908EDT   RANGE
.ROMULT 10150908EDT 10270908EDT   FGROUP
.RRIMULT 10150908EDT 10270908EDT   FGROUP
.SWITCHTS 10150908EDT 10270908EDT   FGROUP

I have tried various things with sub and gsub, including a less-specific regular expression, and that didn't work either. I'm very new to awk, so trying to figure this out has been a good learning experience, but I'm hoping to "cut to the chase" now... :slight_smile: Any help appreciated. Thanks!

how do you pass the 'dateblock' variable into awk?
For example

awk ' ( NF == 5 ) && ( /^\./ ) {  gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock, $4) ; print } ' dateblock=$(date +%Y%m%d) modtest

I defined dateblock earlier in the program, before the awk statement. Unfortunately, the desired hour number is not just system time, but one of (02, 08, 14, 20) under EDT and (01, 07, 13, 19) under EST. I have code earlier in the program that figures out which one is appropriate based on the current system time.

Then just pass it into awk as noted above.

OK, that helped -- thanks!

I was under the impression that gsub would make the substitutions in the matching lines in the input file itself, but that the output would contain both the changed and unchanged lines. All I've been able to make it do so far is output the lines it changes. By chance are there any switches in gsub that might make it do what I want, or do I need to figure out some other way to make it pick up the additional unchanged lines in the proper order?

awk ' ( NF == 5 ) && ( /^\./ ) {  gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock, $4) } 1' dateblock=$(date +%Y%m%d) modtest

I have been unable to find documentation to tell me how that works exactly -- it seems to print the entire file as well as the changed lines. I can get rid of the duplicates with uniq, but since I need to run a similar command on the same file 4 times, is there a way to have it only print the entire file once?

it should print the entire file once with the changed fields. It works fine on your sample data in the original post.
Please show WHAT you have and HOW do you run it.

awk ' ( NF == 5 ) && ( /^\./ ) {  gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock synoptime tz, $4) ; print  } 1' dateblock=$(date +%m%d%y)     tz=$(date +%Z) synoptime=$synoptime modtest > modtest2

awk ' (( NF == 4 ) && ( /^\./ )) && ( $4 !~ /^[0-9]/ ) {  gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock synoptime tz, $3) ; print } 1' dateblock=$(date +%m%d%y)     tz=$(date +%Z) synoptime=$synoptime modtest >> modtest2

awk ' (( NF == 4 ) && ( /^\./ )) && ( $4 ~ /^[0-9]/ ) {  gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock synoptime tz, $4) ; print } 1' dateblock=$(date +%m%d%y)     tz=$(date +%Z) synoptime=$synoptime modtest >> modtest2

awk ' (( NF == 3 ) && ( /^\.T/ )) && ( $3 ~ /^[0-9]/ ) {  gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock synoptime tz, $3) ; print } 1' dateblock=$dateblock tz=$tz synoptime=$synoptime modtest >> modtest2

I have also tried it as one big awk block, but so far it's only outputting the last line of the modtest file:

(synoptime, dateblock, tz defined earlier in script)

awk 'BEGIN {dateblock=$dateblock; tz=$tz; synoptime=$synoptime}
{
     if  (( NF == 5 ) && ( /^\./ ))
     { gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock synoptime tz, $4) }

     if  ((( NF == 4 ) && ( /^\./ )) && ( $4 !~ /^[0-9]/ ))
     { gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock synoptime tz, $3) }

     if ((( NF == 4 ) && ( /^\./ )) && ( $4 ~ /^[0-9]/ ))
     { gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock synoptime tz, $4) }

     if ((( NF == 3 ) && ( /^\.T/ )) && ( $3 ~ /^[0-9]/ )) 
     { gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock synoptime tz, $3) }
}
END { print } ' modtest
awk '
( NF == 5 ) && ( /^\./ ) {
   gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock synoptime tz, $4) }

( NF == 4 ) && ( /^\./ ) && ( $4 !~ /^[0-9]/ ) {
   gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock synoptime tz, $3) }

( NF == 4 ) && ( /^\./ ) && ( $4 ~ /^[0-9]/ ) {
   gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock synoptime tz, $4) }

( NF == 3 ) && ( /^\.T/ ) && ( $3 ~ /^[0-9]/ ) {
   gsub(/[0-1][0-9][0-3][0-9][0-9][0-9][0-2][0-9][E][DS][T]/, dateblock synoptime tz, $3) }
1
' dateblock="$(date +%m%d%y)" tz="$(date +%Z)" synoptime="$synoptime" modtest > modtest2

Perfect! Many thanks!