2
  • Awk version: GNU Awk 3.1.1
  • Platform: OpenVMS 8.4-L1
  • Awk knowledge: limited

Script (Awk one liner). NOTE: OpenVMS parameter parsing requires quotes

gawk "-F" ";" "BEGIN {x = 0} ; x++ {print $0,$1"";""x}" in.file > out.file

**in.file**
file.log;2000
file.log;1999
file.log;1998
file.log;1997

**out.file**
file.log;1999 file.log;2
file.log;1998 file.log;3
file.log;1997 file.log;4

I wanted the following instead

file.log;2000 file.log;1
file.log;1999 file.log;2
file.log;1998 file.log;3
file.log;1997 file.log;4

If I change the awk command above to: ...{x = -1}... I get

**out.file**
file.log;2000 file.log;0
file.log;1998 file.log;2
file.log;1997 file.log;3

What I am wondering is

  1. Can someone else using a different platform test this to see if it produces the same output?
  2. Could it be the awk version I am running that is at fault OR is there something wrong with my awk script that I am not understanding

It really seems to me that it should produce a file.log;1999 file.log;1 line but it doesn't. I'm at a loss and need some pointers / awk education

Thanks in advance :-)

CW Holeman II
  • 4,661
  • 7
  • 41
  • 72
IanD
  • 43
  • 1
  • 7

4 Answers4

2

You need to use ++x in place of x++. The x++ initially evaluates to 0 (but increments x to 1), and 0 is false, so the action is not taken; nothing is printed for the first line.

When you initialize x to -1, the first line is printed because x++ evaluates to -1 which is true; the second is skipped because x is 0, etc.

You don't need the BEGIN block. Variables are auto-vivified with zero (or the empty string) as the value. And in fact you don't need to trigger the printing based on whether x is zero or not; you simply want to always print an incremented value of x.

So, on a Mac (Unix-like) system, I can run:

$ gawk "-F" ";" '{print $0,$1";"++x}' file.in
file.log;2000 file.log;1
file.log;1999 file.log;2
file.log;1998 file.log;3
file.log;1997 file.log;4
$

Translated to VMS conventions, that would be:

gawk "-F" ";" "{print $0,$1"";""++x}" file.in
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • That worked perfectly - Thank you :-) I did try stuff with the ++x (although I used x++) but i could not get the syntax right I really appreciate the help. I'm going to try different combinations of what you have listed to hopefully cement why it works. It's clear my novice understanding of awk was to blame Your solution is far more elegant too :-) – IanD Mar 31 '17 at 05:31
1

For understanding what happened, you can put additional output.

$ gawk -F\; 'BEGIN {x = 0} {print "step 1: "x} x++ {print "step 2: "x; print $0,$1";"x} {print "step 3: "x"\n"}' in.file
step 1: 0
step 3: 1

step 1: 1
step 2: 2
file.log;1999 file.log;2
step 3: 2

step 1: 2
step 2: 3
file.log;1998 file.log;3
step 3: 3

step 1: 3
step 2: 4
file.log;1997 file.log;4
step 3: 4

So there is the effect of the post-increment.

x {something} is same as {if(x){something}}. So value of x will be use as condition for calling block {something}.

On first line, on step 1, x++ will return 0 and increment 'x', which will skip step 2. But on step 3 'x' will be 1.

On second line, on step 1, x++ will return 1 and increment 'x'. On step 2, x will be already '2'. That's what you get.

You can fix it like this:

gawk -F\; 'BEGIN {x = 0} {print $0,$1";"++x}' in.file
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
komar
  • 861
  • 5
  • 8
0

"";"" is the problem. Try. 'BEGIN ... $1";"x ... instead. What you posted amounts to. print $0,$1; x} I.e. throw x awayt. Hth

Mischa
  • 2,240
  • 20
  • 18
  • I think you'll find that the VMS command-line convention for escaping double quotes is to double them up instead of using the backslash as on Unix. I don't know that — but if you treated the `"";""` sequence as a way of getting a semicolon into the output it does make sense. – Jonathan Leffler Mar 31 '17 at 04:51
  • Unfortunately, the OpenVMS command line needs the "";"" to output a quote If I drop back to the single quote, the output does not include the ; and value of x (i.e. I get .log and not .log;1 etc) The output is working except when x becomes 1 for some reason – IanD Mar 31 '17 at 04:59
0

Since you don't know AWK too well, may I suggest to skip it altogether and switch to PERL?

But first things first. What problem are you really trying to solve? Seems to me you obtained a list of file names (DIR/COL=2/OUT=x.x) You want to use that list to generate a rename where the file with the highest version number becomes number 1, the next down 2 and so on. Is that correct?

I hope there no need to worry about overlap perhaps thanks to a version limit in place.

DCL defaults can do that just fine. - Using PERL here just as a handy way to create a bunch of files - Using FILE_ID to show which file is which Here it goes.

$perl -e "open X,qq(>file.log;$_) for 1997..2000"
$dir/file
FILE.LOG;2000        (59705,105,0)
FILE.LOG;1999        (46771,399,0)
FILE.LOG;1998        (42897,980,0)
FILE.LOG;1997        (24538,519,0)
$rena/log file.log.* tmp.log;
%RENAME-I-RENAMED, FILE.LOG;2000 renamed to TMP.LOG;1
%RENAME-I-RENAMED, FILE.LOG;1999 renamed to TMP.LOG;2
%RENAME-I-RENAMED, FILE.LOG;1998 renamed to TMP.LOG;3
%RENAME-I-RENAMED, FILE.LOG;1997 renamed to TMP.LOG;4
$rena tmp.log;* file.log/log
%RENAME-I-RENAMED, TMP.LOG;4 renamed to FILE.LOG;4
%RENAME-I-RENAMED, TMP.LOG;3 renamed to FILE.LOG;3
%RENAME-I-RENAMED, TMP.LOG;2 renamed to FILE.LOG;2
%RENAME-I-RENAMED, TMP.LOG;1 renamed to FILE.LOG;1
$dir/file file.log;*
FILE.LOG;4           (24538,519,0)
FILE.LOG;3           (42897,980,0)
FILE.LOG;2           (46771,399,0)
FILE.LOG;1           (59705,105,0)

ok? No helpers needed. Just two commands relying on the ";" magic to stop inheritance.

Now let's see how to do this in Perl directly.

$ dir/file
FILE.LOG;2000        (59705,105,0)
FILE.LOG;1999        (46771,399,0)
FILE.LOG;1998        (42897,980,0)
FILE.LOG;1997        (24538,519,0)
$ perl -e "for (<file.log;*>) {$i++; $old = $_; s/;\d+/;$i/; rename $old, $_}"
$ dir/file
FILE.LOG;4           (24538,522,0)
FILE.LOG;3           (42897,983,0)
FILE.LOG;2           (46771,402,0)
FILE.LOG;1           (59705,108,0)

Broken down in steps:

<xxx> = glob("xxx") = glob(qq(xxx) = wildcard lookup with interpolated string.
for (<file.log;*>)        # Loop over all files matching pattern, output into $_
{                         # code block
$i++;                     # increment for each iteration. start at 1
$old = $_;                # save the fetched file name
s/;\d+/;$i/;              # substitute a semicolon followed by numbers 
rename $old, $_           # Actual rename. Try with PRINT first.
}                         # end of code block

ok?

Hein
  • 1,453
  • 8
  • 8