简体   繁体   中英

awk: mysterious double line print

I have a $FILE

1 textline1
1 textline2
1 textline3
1 textline4

I want to search for textline3 and make sum: 1stcolumn+1

Linenumber is:

lnr=$(grep -n textline3 $FILE | cut -d : -f1)

But when i use awk:

awk -v l="$lnr" 'FNR==l{ sub($1,$1 + 1); print }1' $FILE

I get the erroneous (3rd line is double printed)

1 textline1
1 textline2
2 textline3
2 textline3
1 textline4

Reason for double printing is , you are printing inside {} and using 1 as well. 1 means true and awk's default behaviour is to print.

awk -v l="$lnr" 'FNR==l{ sub($1,$1 + 1)}1' $FILE

You can avoid the usage of grep and let awk do the magic.

awk '/textline3/{sub($1,$1 + 1)}1' file
1 textline1
1 textline2
2 textline3
1 textline4

In general, you can do a awk '/<pattern>/ { <action> }' file for manipulation.

If you just want to increment the first field on the N th line, just say so:

$ awk -v line=3 'FNR==line {$1+=1}1' file  # line is more readable than l
1 textline1
1 textline2
2 textline3
1 textline4

Note the 1 here is triggering awk 's default action, consisting in printing the current line. For every line it will print the initial value but for the line number line it will print the 1 st value with its value +1.

And that's why you see that line twice in your code: because in the action triggered by FNR==line you are already calling print .

Theres a few problems with your script:

  1. The specific bug you are asking about: You're calling print inside the action block and then the 1 at the end of the script is a constant true condition which will cause awk to execute it's default action of printing the record again.
  2. Never use the letter l (el) as a variable name, it looks far too much like the number 1 (one) and so makes your code harder to read.
  3. You don't need a function call to change $1 , just do $1++ (assuming textline3 doesn't contain chains of white space that must not be concatenated into one blank char).
  4. You don't need to use grep+wc to find lines, just keep it in awk.
  5. In general you should avoid using input data (eg $1 ) as either the search regexp or replacement string fields of any *sub() command as that will fail cryptically given various regexp or backreference metacharacters in the input.

So your script should be just:

$ awk '/textline3/{$1++}1' file
1 textline1
1 textline2
2 textline3
1 textline4

Solution using perl

$ perl -pe 's/^(\d+)(?=\s*textline3)/$1+1/e' $FILE
1 textline1
1 textline2
2 textline3
1 textline4

or

perl -pe 's/^(\d+)/$1+1/e if /textline3/' $FILE

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM