Use awk output as variable for string comparison - macos

I'm filtering the output of ifconfig on a macOS (High Sierra) to trigger an action using a simple if statement, though the action isn't triggered although the corresponding strings appear to match (when tested with echo).
When the script is run, if the network en1 is active, the expected action is to print OK.
I tried:
printf "%s" in the awk command
using a double (and simple) quote for "active"
holding active in a variable for the comparison
replacing en1_status=`...` by en1_status=$(...)
without success.
#!/bin/bash
en1_status=`ifconfig en1 | grep "status: " | awk '{print $2}'`
if [ "$en1_status" = active ]; then
echo OK
fi
And $en1_status seems to match active:
echo $en1_status
active

This generally means that you've got nonprintable characters -- like syntax highlighting, or DOS newlines -- in your variable.
One easy way this can happen is if you've got your grep instance set to always insert color codes, as with --color=always rather than the default (safer) --color=auto.
To track this down, compare the output of the following commands:
printf '%s' "$en1_status" | xxd
printf '%s' inactive | xxd
You can also moot grep's configuration by letting awk do the searching:
en1_status=$(ifconfig en1 | awk '/status: / { print $2 }')

Your solution works on MacOS, if you double quote the right hand side of the if comparison (I replaced inactive for my local test, because that interface is not active locally for me):
~/> cat test.sh
#!/bin/bash
en1_status=`ifconfig en1 | grep "status: " | awk '{print $2}'`
if [ "$en1_status" = "inactive" ]; then
echo OK
fi
And when I run it:
~/> ./test.sh
OK

Related

Multiple whois lookup

I have the below script for whois lookup
for line in $(cat ips.txt)
do
echo $line
whois $line | grep OrgName | awk '{print $2,$NF}'
done
I am having the output
192.168.1.1
Internet Authority
How can I achieve the output in the below format ?
192.168.1.2 : Internet Authority
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04 LTS"
On the 'echo $line' line, the shell was asked to print the value of $line. The shell says ok - done.
Then the shell moves on to the next line, that basically says 'get string then pipe it to some string manipulation and print result'.
I believe 'print something on the screen' was asked from the shell twice, 1 by echo 2 by awk, which are from 2 separate lines , so the shell behaved as expected.
To prevent this you can contain the second line in $(), so that echo will print "$line + $(whatever comes out here)"
for line in $(cat ips.txt)
do
echo $line : $(whois $line | grep OrgName | awk '{print $2,$NF}')
done

Why does nesting this awk command with eval produce a different result than running it?

I have this script that's designed to assign variables to commands that collect information about a system and then echo them back. This works very well for the first few commands, but the last one continues to return the value without "PRETTY_NAME=" stripped out of the output.
Is there some problem with this that I'm not seeing?
I have tried using grep to separate awk:
grep PRETTY_NAME /etc/*-release | awk -F '=' '{print $2}'
Using escaped quotes:
awk -F \"=\" '/PRETTY_NAME/ {print $2}' /etc/*-release
Whole block (edited somewhat for relevance)
declare -A CMDS=(
[primaryMacAddress]="cat /sys/class/net/$(ip route show default | awk '/default/ {print $5}')/address"
[primaryIpAddress]="hostname --ip-address"
[hostname]="hostname"
[osType]="awk -F '=' '/PRETTY_NAME/ {print $2}' /etc/*-release"
)
#This bit is actually nested in another function
for kpair in "${!CMDS[#]}" do
echo "$kpair=\"$( eval ${CMDS[$kpair]} )\""
done
Results when run from .sh file:
osType="PRETTY_NAME="Red Hat Enterprise Linux Server 7.4 (Maipo)""
expected:
osType=""Red Hat Enterprise Linux Server 7.4 (Maipo)""
When this command is run by itself, it seems to work as intended:
$ awk -F '=' '/PRETTY_NAME/ {print $2}' /etc/*-release
"Red Hat Enterprise Linux Server 7.4 (Maipo)"
Because your Awk command is specified in double quotes, interior dollar signs are subject to special treatment: the $2 is treated as a parameter substitution by your shell, and so the array element doesn't store the text $2 but rather its expansion. The Awk interpreter never sees the $2 syntax.
However, you have a second problem in your command dispatcher. Your eval command does not prevent word splitting:
eval ${CMDS[$kpair]}
you want this:
eval "${CMDS[$kpair]}"
without the quotes, your command is arbitrarily chopped into fields on whitespace. Then eval catenates the pieces together, using one space between them, and evaluates the resulting syntax. The difference can be demonstrated with the following example:
$ cmd="awk '/foo/ { print \$1\" \"\$2 }'"
$ echo 'foo a' | eval $cmd
foo a
$ echo 'foo a' | eval "$cmd"
foo a
We can just use echo to understand the issue:
$ echo $cmd
awk '/foo/ { print $1" "$2 }'
$ echo "$cmd"
awk '/foo/ { print $1" "$2 }'
The substitution of $cmd and the subsequent word splitting is done irrespective of any shell syntax that `cmd contains. We can see the pieces like this:
$ for x in $cmd ; do echo "<$x>" ; done
<awk>
<'/foo/>
<{>
<print>
<$1">
<"$2>
<}'>
When we execute eval $cmd, the above pieces are generated and re-combined by eval and evaluated. Needless to say, you don't want your command syntax to be chopped up and re-combined like this; who knows what sort of hidden bug will arise. It may be okay for the commands you have now, but as a generic command dispatch mechanism, it is flawed.

Trying to kill processes in bash - code embedded in [...] not run?

here is what I want to do
while ["ps a | grep '[m]ono' | awk '{print $1}'" != ""] ; do
kill ps a | grep '[m]ono' | awk '{print $1}'
done
meaning if the grep returns nothing, don't try a kill.
The thing is I've always been lost with the expression evaluation in bash, sometime I use " " around something, sometime it's eval, sometime it's ''.
Could someone explain to me how to write the condition in my loop and explain the difference between the above? I'm used to find the working one with many tries and it feels like a huge loss of time.
Best choice: Do something else.
Utilities already exist for this purpose. Example:
killall mono
pkill mono
...or, even better, something targeted to the specific executable you want to terminate:
fuser -k /path/to/something.exe
...which would kill only programs with a file handle on that specific executable, rather than all applications running with mono on the machine.
...but, to explain the bugs:
There are two things wrong here: Missing command substitutions, and missing whitespace.
Missing whitespace:
["ps a | grep '[m]ono' | awk '{print $1}'" != ""]
...is literally trying to run a command with a name starting with [ps, as in, looking in the PATH for...
/bin/[ps\ a
/usr/bin/[ps\ a
...etc. [ is a command, and needs a space after its name like any other command. Thus:
[ "ps a | grep '[m]ono' | awk '{print $1}'" != "" ]
...fixes this problem (but leaves another one).
Missing command substitutions:
[ "ps a | grep '[m]ono' | awk '{print $1}'" != "" ]
...is comparing a string that starts with "ps a" to to ""; it does not compare the output of running a command that starts with ps a. To do that, you'd instead run:
[ "$(ps a | grep '[m]ono' | awk '{print $1}')" != "" ]
The content of $(...) is replaced with the output of the command within; thus, running your pipeline and comparing its output to an empty string.

CSV Two Column List With Spaces. Need everything before or everything after in two separate variables

I have a CSV list that is two columns (col1 is Share Name, col2 is file system path). I need two variables for either everything BEFORE the comma, or everything AFTER the column. My issue is that either column potentially has spaces, and even though these are quoted in the output, my script isn't handling them properly.
CSV:
ShareName,/path/to/sharename
"Share with spaces",/path/to/sharewithspaces
ShareWithSpace,"/path/to/share with spaces"
I was using this awk statement to get either field 1 or field 2:
echo $line | awk -F "\"*,\"*" '{print $2}'
BUT, I soon realized that it wasn't handling the spaces properly, even when passing that command to a variable and quoting the variable.
So, then after googling my brain out, I was trying this:
echo $line | cut -d, -f2
Which works, EXCEPT when echoing the variable $line. If I echo the string, it works perfectly, but unfortunately I'm using this in a while/read/do.
I am fairly certain my issue is having to define fields and having whitespace, but I really only need before or after a comma.
Here's the stripped down version so there's no sensitive data.
#!/usr/bin/bash
ssh <ip> <command> > "2_shares.txt"
<command> > "1_shares.txt"
file1="1_shares.txt"
file2="2_shares.txt"
while read -r line
do
share=`echo "$line" | awk -F "\"*,\"*" '{print $1}'`
path=`echo "$line" | awk -F "\"*,\"*" '{print $2}'`
if grep "$path" $file2 > /dev/null;
then
:
else
echo "SHARE NEEDS CREATED FOR $line"
case $path in
*)
blah blah blah
;;
esac
fi
done < "$file1"
You could simply do like this,
awk -F',' '{print $2}' file
To skip the first line.
awk -F',' 'NR>1{print $2}' file
Your issue is simply that you aren't quoting your shell variables. ALWAYS quote shell variables unless you have a very specific reason not to and are fully aware of all of the consequences.
I strongly suspect the rest of your script is completely wrong in it's approach since you apparently didn't know to quote variables and are talking about shell loops and echoing one line at time to awk so please do post a followup question if you'd like help.

Bash disagrees with my pipes, refuses to do the full command

This is my code so far:
#! /bin/bash
Options=("1" "2" "3")
select opt in "${Options[#]}"
do
case "$REPLY" in
1)
who=$(whoami)
echo -e "you are: $who"
;;
2)
var=$(uptime | awk '{print $3}' | cut -d, -f 1)
echo $var
;;
3) break;;
*) echo "Invalid input";;
esac
done
It doesn't work. The first option works just fine. The second one however doesn't.
Further testing suggests bash doesn't like piping much, the output is simply "#", suggesting it's only the awk part of the command that actually gets executed. The command just works fine out in the shell (it reports the time it has been online only).
This line doesn't make any sense:
var=$(uptime | awk '{print 3}' | -d, -f 1)
The awk statement should be
awk '{print $3}'
and the cut statement should be
cut -d ',' -f 1
To be honest, you probably don't need the awk statement at all for what it seems you want to do. Just pipe uptime to the cut statement above.
var=$(uptime | cut -d ',' -f 1)
This happens when you use a text files from Windows/Dos and reuse it in unix.
The line terminators in each respective OS are different.
HTH
p.s. You'll notice a ^M at the end of a line when you open the text file in vi/vim.
Doesn't work says "cut: invalid byte or field list" – user2230627 Apr 1 at 3:34
:

Resources