where to put brackets in awk - awk

Hello every one I want to ask that I am very very confused about the brackets {} in awk like I have written a code
{
FNR == 3 { print $1 " age is " $2 }
}
but it gave me error on outer brackets but didn't give error on the brackets around the print statement why is it so :/ also in the following code
{
s = $1
d = $2
no = $1 + $2
{print no}
}
when I remove outer brackets my arguments displayed, the number of LOC times why is it I am very confuse kindly help me
thanks

An awk script consists of commands. Each command has a pattern and an action:
pattern1 { action1 }
pattern2 { action2 }
For each line in the input, awk tests each pattern and performs the corresponding action when the pattern is true.
The pattern can be omitted, in which case it is taken as always true and the action is performed for each line. Similarly, the action can be omitted, in which case it is taken as a print; this lets you easily use awk to select lines without changing the lines.
With this structure in mind, we can interpret the given examples. The first one is a single action that is applied to every line. But the action isn't well formed---if you remove the outer brackets, it becomes a distinct pattern and action, both of which are correctly constructed.
The second example also is applied to every line. It takes the first two (whitespace separated) fields from the lines, adds them as numbers, and prints the result. Removing the outer brackets gives you three patterns without corresponding actions, and an action without a pattern. Thus, the patterns---which are the value of the assignments, and usually true---have an implicit print that is usually invoked. Similarly, the action is always invoked, printing the value of no.

Related

Multiple awk pattern matching in one line

Let's say I want to match foo and bar in a file. The following works :
/foo/{commands}/bar/{commands}
Note: here there is no separator between /foo/{commands} and /bar/{commands}.
The following is also okey:
/foo/{commands1}{commands2} where {commands2} is executed for every line and I've left out the pattern.
But what if I want to leave out the commands? What's awk's syntax rule here? The following doesn't work:
/foo//bar/
Of course, I could write it as /foo/{print}/bar/{print}, but I was wondering what's the delimiter for separating segments and why you sometimes need it and sometimes you don't.
awk works on method of regexp then action in this if we are mentioning /..../ and not mentioning any action so when condition is TRUE then by default print of line will happen. In case you want to put this into 2 different statements try like:
awk '/foo/;/bar/' Input_file
Above will mean like:
Since they are segregated with ; these will be treated as 2 different conditions.
When /foo/ is true for any line then NO action is mentioned so print of that line will happen.
When /bar/ is true for any line same thing for this also, condition is true and no action mentioned so print of line will happen.
But point to be noted that in case any line has both strings in it so that line will be printed 2 times, which I believe you may NOT want it so you could do like following:
OR within single condition itself try something like:
awk '/foo|bar/' Input_file
Or in case you need to check if strings present in same line then try Like:
awk '/foo/ && /bar/' Input_file
To match foo and bar in a file - just combine patterns:
awk '/foo/ && /bar/ ....'

AWK script, linefeed under Windows causing different function

I have a simple AWK script which I try to execute under Windows. Gnu AWK 3.1.6.
The awk script is run with awk -f script.awk f1 f2 under Windows 10.
After spending almost half a day debugging, I came to find that the following two scenarios produce different results:
FNR==NR{
a[$0]++;cnt[1]+=1;next
}
!a[$0]
versus
FNR==NR
{
a[$0]++;cnt[1]+=1;next
}
!a[$0]
The difference of course being the linefeed at line 1.
It puzzles me because I don't recall seeing anywhere awk should be critical about linefeeds. Other linefeeds in the script are unimportant.
In example one, desired result is achieved. Example 2 prints f1, which is not desred.
So I made it work, but would like to know why
From the docs (https://www.gnu.org/software/gawk/manual/html_node/Statements_002fLines.html)
awk is a line-oriented language. Each rule’s action has to begin on
the same line as the pattern. To have the pattern and action on
separate lines, you must use backslash continuation; there is no other
option.
Note that the action only has to begin on the same line as the pattern. After that as we're all aware it can be spread over multiple lines, though not willy-nilly. From the same page in the docs:
However, gawk ignores newlines after any of the following symbols and
keywords:
, { ? : || && do else
In Example 2, since there is no action beginning on the same line as the FNR == NR pattern, the default action of printing the line is performed when that statement is true (which it is for all and only f1). Similarly in that example, the action block is not paired with any preceding pattern on its same line, so it is executed for every record (though there's no visible result for that).

Add a number by subtracting an existing number by awk

I would like to convert
Title Page/4,Black,notBold,notItalic,open,TopLeftZoom,0,0,0.0
Contents/16,Black,notBold,notItalic,open,TopLeftZoom,0,0,0.0
to
Title Page 1/4,Black,notBold,notItalic,open,TopLeftZoom,0,0,0.0
Contents 13/16,Black,notBold,notItalic,open,TopLeftZoom,0,0,0.0
The rule is to subtract the number following / by 3 and add that result in front of /.
I tried to do that with awk.
awk -F',/' '{gsub(/\//, ($2-10) + "\/"}' myfile
but it doesn't work. Why is it? Thanks.
A slight modification to your attempt produces the desired output:
$ awk -F'[,/]' '{sub(/\//, " " ($2-3) "/") }1' file
Title Page 1/4,Black,notBold,notItalic,open,TopLeftZoom,0,0,0.0
Contents 13/16,Black,notBold,notItalic,open,TopLeftZoom,0,0,0.0
-F is used to specify the input field separator. I have changed it to a regex group which matches commas and slashes, which means that the second field $2 contains the number that you are trying to replace. As you are only interested in making a single substitution in each record, I have used sub rather than gsub. Note that in awk, strings are automatically concatenated (you shouldn't use +).
Awk programs are stuctured like condition { action }. If no condition is specified, the action block is always run. If no action is specified, the default action is { print }, which prints the record. In the above script, 1 is used to print the record, as it is the simplest expression that evaluates to true.

Why does "1" in awk print the current line?

In this answer,
awk '$2=="no"{$3="N/A"}1' file
was accepted. Note the 1 at the end of the AWK script. In the comments, the author of the answer said
[1 is] a cryptic way to display the current line.
I'm puzzled. How does that work?
In awk,
Since 1 always evaluates to true, it performs default operation {print $0}, hence prints the current line stored in $0
So, awk '$2=="no"{$3="N/A"}1' file is equivalent to and shorthand of
awk '$2=="no"{$3="N/A"} {print $0}' file
Again $0 is default argument to print, so you could also write
awk '$2=="no"{$3="N/A"} {print}' file
In-fact you could also use any non-zero number or any condition which always evaluates to true in place of 1
The documentation says
In an awk rule, either the pattern or the action can be omitted, but not both. If the pattern is omitted, then the action is performed for every input line. If the action is omitted, the default action is to print all lines that match the pattern.
So, it treats 1 as pattern with no action. The default action is to print the line.
Even if you have a couple of rules, like in
awk '
in_net {
if (/^\s+bindIp:/) {
print " bindIp: 0.0.0.0"
next
} else if (/^\s*(#.*)?$/) {
in_net = 0
}
}
/^net:/ {
in_net = 1
}
1
' /etc/mongod.conf
You still need 1, since default action is triggered only when encountering rule with no action.
AWK works on method of condition and then action. So if any condition is TRUE any action which we mention to happen will be executed then.
In case of 1 it means we are making that condition TRUE and in this case we are not mentioning any action to happen, so awk's by default action print will happen.
So this is why we write 1 in shortcut actually speaking.
I thought I’d add an answer that explains how this shorthand works in terms of the POSIX specification for awk:
Basic description:
An awk program is composed of pairs of the form:
pattern { action }
Missing action:
Either the pattern or the action (including the enclosing brace characters) can be omitted.
A missing pattern shall match any record of input, and a missing action shall be equivalent to:
{ print }
Description of pattern
A pattern is any valid expression
Description of Expression patterns:
An expression pattern shall be evaluated as if it were an expression in a
Boolean context. If the result is true, the pattern shall be considered to
match, and the associated action (if any) shall be executed.
Boolean context:
When an expression is used in a Boolean context, if it has a numeric value,
a value of zero shall be treated as false and any other value shall be
treated as true. Otherwise, a string value of the null string shall be
treated as false and any other value shall be treated as true.
In the example of awk '$2=="no"{$3="N/A"}1', the pattern of the first pair is $2=="no" with a corresponding action of $3="N/A". This leaves 1 by itself as the next “pair” (pattern without a corresponding action).
Instead of 1, this lone expression pattern could be any numeric value or non-empty string, e.g.,
awk 9999
awk '"string"'
The awk 1 short-hand is fine when typing one-liners in an interactive shell. On the other hand, when writing scripts, I prefer my code to be more maintainable and readable for others by using the more explicit awk '{ print }'.

How do you get awk to move to next line when a condition has been actioned?

I'm trying to write an awk script which checks certain conditions and throws away lines meeting those conditions.
The specific condition are to throw away the first two lines of the file and any line that starts with the text xyzzy:. To that end, I coded up:
awk '
NR < 2 {}
/^xyzzy:/ {}
{print}'
thinking that it would throw away the lines where either of those two conditions were met and print otherwise.
Unfortunately, it appears that the print is being processed even when the line matches one of the other two patterns.
Is there a C-like continue action that will move on the next line ignoring all other condition checks for the current line?
I suppose I could use something like ((NR > 1) && (!/^xyzzy:/)) {print} as the third rule but that seems rather ugly to me.
Alternatively, is there another way to do this?
Use the keyword next as your action
This keyword is often useful when you want to iterate over 2 files; sometimes it's the same file that you want to process twice.
You'll see the following idiom:
awk '
FNR==NR {
< stuff that works on file 1 only >
next
}
{
< stuff that works on file 2 only >
}' ./infile1 ./infile2