awk: passing variables from bash - awk

I am getting syntax errors with the following code. Is there an awk version that does not support the "-v" option or am I missing something? Thanks.
#!/usr/local/bin/bash
f_name="crap.stat"
S_Date="2012-02-10"
E_Date="2012-02-13"
awk -F "\t" -v s_date="$S_Date" -v e_date="$E_Date" 'BEGIN {print s_date,e_date}' $f_name

Your code completely works on my awk (GNU Awk 3.1.6).
There is another way though, If you export your variables you can use it in ENVIRON array
$ export f_name="crap.stat"
$ awk '{ print ENVIRON["f_name"] }' anyfile
crap.stat

The default awk program on Solaris 10 (aka oawk) does not seem to support the -v option; the alternative nawk program does support it. Some people switch the name awk so it is a link to nawk, so you can't readily predict which you'll find as awk.
The awk programs on HP-UX 11.x, AIX 6.x and Mac OS X (10.7.x) all support the -v notation, which isn't very surprising since POSIX expects support for -v.

Related

How to insert argument in awk script?

I'm writing a shell script which shut down some services and trying to get its pid by using the following awk script.
However, this awk script can't get pid. What's wrong with that?
ps -ef | awk -v port_no=10080 '/[m]ilk.*port=port_no/{print $2}'
The result of ps -ef is like this:
username 13155 27705 0 16:06 pts/2 00:00:00 /home/username/.rbenv/versions/2.3.6/bin/ruby /home/username/.rbenv/versions/2.3.6/bin/milk web --no-browser --host=example.com --port=10080
This process is working with a different port argument as well, so I want to kill the process only working on port=10080.
The awk script below works fine, but when I specify the port no using awk -v like the above, it doesn't work well.
ps -ef | awk '/[m]ilk.*port=10080/{print $2}'
awk version: GNU Awk 4.0.2
The syntax for pattern matching with /../ does not work with variables in the regular expression. You need to use the ~ syntax for it.
awk -v port_no=10080 '$0 ~ "[m]ilk.*port="port_no{print $2}'
If you notice the regex carefully, the regex string on the r.h.s of ~ is under the double-quotes ".." except the variable name holding the port number which shouldn't be under quotes, for the expansion to happen.
This task is easily accomplished using pgrep:
$ pgrep -f '[m]ilk.*port=10080'
Have a look at man pgrep for details.

How does gawk -e 'BEGIN {' -e 'print "hello" }' work?

Gawk 5.0.0 was released on April 12, 2019. Going through the announcement I found this:
Changes from 4.2.1 to 5.0.0
(...) 11. Namespaces have been implemented! See the manual. One consequence of this is that files included with -i, read with -f, and command line program segments must all be self-contained syntactic units. E.g., you can no longer do something like this:
gawk -e 'BEGIN {' -e 'print "hello" }'
I was curious about this behaviour that is no longer supported, but unfortunately my Gawk 4.1.3 did not offer much output out of it:
$ gawk -e 'BEGIN {' -e 'print "hello" }'
gawk: cmd. line:1: BEGIN {
gawk: cmd. line:1: ^ unexpected newline or end of string
From what I see in the manual of GAWK 4.2, the -e option was marked as problematic already:
GNU Awk User's Guide, on Options
-e program-text
--source program-text
Provide program source code in the program-text. This option allows you to mix source code in files with source code that you enter on the command line. This is particularly useful when you have library functions that you want to use from your command-line programs (see AWKPATH Variable).
Note that gawk treats each string as if it ended with a newline character (even if it doesn’t). This makes building the total program easier.
CAUTION: At the moment, there is no requirement that each program-text be a full syntactic unit. I.e., the following currently works:
$ gawk -e 'BEGIN { a = 5 ;' -e 'print a }'
-| 5
However, this could change in the future, so it’s not a good idea to rely upon this feature.
But, again, this fails in my console:
$ gawk -e 'BEGIN {a=5; ' -e 'print a }'
gawk: cmd. line:1: BEGIN {a=5;
gawk: cmd. line:1: ^ unexpected newline or end of string
So what is gawk -e 'BEGIN {' -e 'print "hello" }' doing exactly on Gawk < 5?
It's doing just what you'd expect - concatenating the parts to form gawk 'BEGIN {print "hello" }' and then executing it. You can actually see how gawk is combining the code segments by pretty-printing it:
$ gawk -o- -e 'BEGIN {' -e 'print "hello" }'
BEGIN {
print "hello"
}
That script isn't useful to be written in sections and concatenated but if you consider something like:
$ cat usea.awk
{ a++ }
$ echo foo | gawk -e 'BEGIN{a=5}' -f usea.awk -e 'END{print a}'
6
then you can see the intended functionality might be useful for mixing some command-line code with scripts stored in files to run:
$ gawk -o- -e 'BEGIN{a=5}' -f usea.awk -e 'END{print a}'
BEGIN {
a = 5
}
{
a++
}
END {
print a
}

pass shell variable into awk's patern search

In a script I want to search connections established between some ports gathered with another command and set on PORT variable and specific systems.
the PORT variable is pass to awk using -vp=${PORT}
but I don't know how to use "p" it inside the rest of the pattern.
his does not work:
$ lsof -i -P|awk -vp=${PORT} '$(NF-1)~/vm7.+:'$p'->(vm9|vm11).+ESTABLISHED/{print $(NF-1)}'
$ lsof -i -P|awk -vp=${PORT} '$(NF-1)~/vm7.+:'p'->(vm9|vm11).+ESTABLISHED/{print $(NF-1)}'
give this a try:
awk -v p="$PORT" '{pat="yourHost(or whatever):"p}$(NF-1)~pat{print $(NF-1)}'
build the pattern(pat) with p and check the field (NF-1)
you don't need (shouldn't have) the ESTABLISHED in pattern, since it is the last field NF instead of NF-1
Use match:
$ awk -v p=$port 'match($(NF-1),"vm7.+:" p "->(vm9|vm11)"){print $(NF-1)}'
There might be some errors as there was no test material. Removed the ESTABLISHED as it is in $NF, not $(NF-1) (in my systems, at least).
... or don't:
$ awk -v p=$port '$(NF-1) ~ "vm7.+:" p "->(vm9|vm11)" {print $(NF-1)}'
Today I learned something.

"awk" Command Behaves Differently On SuSE 11 vs. Solaris 10

Friends,
I'm trying to extract the last part of following path in a ksh script:
TOOL_HOME=/export/fapps/mytool/mytool-V2-3-4
I want to extract the version # (i.e., 2-3-4) from the above.
awk runs fine on SuSE:
echo $TOOL_HOME | awk -F'mytool-V' '{print $2}'
#2-3-4
However, on Solaris 10, it produces the following:
#ytool
So on Solaris, awk is ignoring everything after the first character in -F'mytool-V'
What should i do to get the same output on both OS's?
On Solaris use /usr/xpg4/bin/awk, not /bin/awk (aka "old, broken awk").
Solaris awk is broken...
$ echo "$TOOL_HOME" | awk '{sub(/.*mytool-V/,"")}1'
2-3-4
or simply with sed
$ echo "$TOOL_HOME" | sed 's/.*mytool-V//'
2-3-4
No need to use awk or any other external program. ksh can do that:
echo ${TOOL_HOME##*mytool-V}

Awk replacement pieces size limit

Trying to find a single word and replace it with the contents of a file. Works on MacOS, but not under linux.
Here is the awk that fails under linux:
awk -v var="${blah}" '{sub(/%WORD%/,var)}1' file.xml
(file.xml is 122 lines, 4.7K)
Error is:
awk: program limit exceeded: replacement pieces size=255
Same file.xml under MacOS, using a slightly different awk works fine:
awk -v var="${blah//$'\n'/\\n}" '{sub(/%WORD%/,var)1}'
Recompiling awk is not an option. This is Ubuntu 12.04, 32-bit.
You could use sed
FILE=`cat Filename`
sed "s/WORD/${FILE}/g" file.xml > newfile.xml
Turns out that good old 'replace' out performs awk in this use case--who would have thought?
replace -v "%WORD%" "$blah" -- file.xml
Using Gnu Awk version 4, and the readfile extension:
gawk -f a.awk file.xml
where a.awk is:
#load "readfile"
BEGIN{
var = readfile("blah")
if (var == "" && ERRNO != "")
print("problem reading file", ERRNO) > "/dev/stderr"
}
{
sub(/%WORD%/,var)
print
}