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

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}

Related

Running an awk command with $SHELL -c returns different results

I am trying to use awk to print the unique lines returned by a command. For simplicity, assume the command is ls -alh.
If I run the following command in my Z shell, awk shows all lines printed by ls -alh
ls -alh | awk '!seen[$0]++'
However, if I run the same command with $SHELL -c while escaping the ! with backslash, I only see the first line of the output printed.
$SHELL -c "ls -alh | awk '\!seen[$0]++'"
How can I ensure the latter command prints the exact same outputs as the former?
EDIT 1:
I initially thought the ! could be the issue. But changing the expression '!seen[$0]++' to 'seen[$0]++==0' has the same problem.
EDIT 2:
It looks like I should have escaped $ too. Since I do not know the reason behind it, I will not post an answer.
In the second form, $0 is being treated as a shell variable in the double-quoted string. The substitution creates an interestingly mangled awk command:
> print $SHELL -c "ls -alh | awk '\!seen[$0]++'"
/bin/zsh -c ls -alh | awk '!seen[-zsh]++'
The variable is not substituted in the first form since it is inside single quotes.
This answer discusses how single- and double-quoted strings are treated in bash and zsh:
Difference between single and double quotes in Bash
Escaping the $ so that $0 is passed to awk should work, but note that quoting in commands that are parsed multiple times can get really tricky.
> print $SHELL -c "ls -alh | awk '\!seen[\$0]++'"
/bin/zsh -c ls -alh | awk '!seen[$0]++'

Bash Extract first number of a three digit version

I would like to extract number 10 from 10.3.0 for Makefile to add specific CFLAGS
Below code is printing only 1030
echo "gcc.exe (Rev5, Built by MSYS2 project) 10.3.0"|sed -r 's/.* ([0-9])/\1/g' | sed -r 's/\.//g'
1030
How to get the 10
A simple awk:
echo "gcc.exe (Rev5, Built by MSYS2 project) 10.3.0" | awk '{print int($NF)}'
10
Or if you must use sed only then:
echo "gcc.exe (Rev5, Built by MSYS2 project) 10.3.0" |
sed -E 's/.* ([0-9]+).*/\1/'
10
Just a tiny tweak to your own solution would do:
echo "gcc.exe (Rev5, Built by MSYS2 project) 10.3.0"|sed -r 's/.* ([0-9])/\1/g' | sed -r 's/\..*//g'
10
Actually the second sed is not needed here:
echo "gcc.exe (Rev5, Built by MSYS2 project) 10.3.0"|sed -r 's/.* ([0-9]+).*/\1/g'
10
What happened is that you replaced things before 10 but not after it, which can be easily fixed.
This solution using the awk functions match() and substr():
echo 'gcc.exe (Rev5, Built by MSYS2 project) 10.3.0' | awk 'match($0, /[[:digit:]]+\./) {print substr($0,RSTART,RLENGTH-1)}'
10
You code using sed gives 1030 as a result because s/.* ([0-9])/\1/g will leave 10.3.0 and then s/\.//g will remove all the dots leaving 1030.
You could match the format ^[0-9]+\.[0-9]+\.[0-9]+$ of the last field $NF, and if it matches split on a dot and print the first part.
echo "gcc.exe (Rev5, Built by MSYS2 project) 10.3.0" | awk 'match($NF, /^[0-9]+\.[0-9]+\.[0-9]+$/) {
split($NF,a,"."); print a[1]
}
'
Output
10
With shown samples, you could try following. Simple explanation would be, make . and ) as field separators and print 3rd field if NF is greater than 2 for that line, to get required output as per shown samples.
echo "gcc.exe (Rev5, Built by MSYS2 project) 10" |
awk -F'\\.|\\) ' 'NF>=2{print $3}'

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 to put this command in a Makefile?

I have the following command I want to execute in a Makefile but I'm not sure how.
The command is docker rmi -f $(docker images | grep "<none>" | awk "{print \$3}")
The command executed between $(..) should produce output which is fed to docker rmi but this is not working from within the Makefile I think that's because the $ is used specially in the Makefile but I'm not sure how to modify the command to fit in there.
Any ideas?
$ in Makefiles needs to be doubled to prevent substitution by make:
docker rmi -f $$(docker images | grep "<none>" | awk "{print \$$3}")
Also, it'd be simpler to use use a singly-quoted string in the awk command to prevent expansion of $3 by the shell:
docker rmi -f $$(docker images | grep "<none>" | awk '{print $$3}')
I really recommend the latter. It's usually better to have awk code in single quotes because it tends to contain a lot of $s, and all the backslashes hurt readability.

awk: passing variables from bash

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.