Bash script greater than or equal to if statement not functioning as expected - bash

The goal of this script is to get the output of my getCpuTemp function, which is currently 60.1, and to send me a push notification via Pushbullet if the CPU temperature is over 70 degrees.
My script currently thinks that 60.1 is over 70, and I'm not sure why, is there something wrong with my if statement?
function getCpuTemp {
sensors cpu_thermal-virtual-0 | awk 'FNR == 3 {print $2}' | tr -d '+\°\C'
}
function sendPushNotification {
curl --header 'Access-Token: <REDACTED>' \
--header 'Content-Type: application/json' \
--data-binary '{"body":"Server CPU Temp is very warm.","title":"WARNING: HIGH TEMP","type":"note"}' \
--request POST \
https://api.pushbullet.com/v2/pushes
}
if getCpuTemp -ge 70; then
echo "ABOVE 70"
sendPushNotification
else
echo "BELOW 70"
fi

As #CharlesDuffy already pointed out in comments, your use of -ge is invalid syntax plus bash doesn't understand floating point numbers but awk does and you're already using it so consider:
cpuTempIsOverOrEqual() {
sensors cpu_thermal-virtual-0 |
awk -v max="$1" 'NR == 3 {exit (($2+0)>=max ? 0 : 1)}'
}
sendPushNotification() {
curl --header 'Access-Token: <REDACTED>' \
--header 'Content-Type: application/json' \
--data-binary '{"body":"Server CPU Temp is very warm.","title":"WARNING: HIGH TEMP","type":"note"}' \
--request POST \
https://api.pushbullet.com/v2/pushes
}
if cpuTempIsOverOrEqual 70; then
echo "ABOVE OR EQUAL TO 70"
sendPushNotification
else
echo "BELOW 70"
fi
I'm assuming above that the output of sensors cpu_thermal-virtual-0 has those characters that you're using tr to remove attached to the end of the 2nd field and, if so, you don't need to do that as awk will strip non-numeric characters from the end of any string you perform a numeric operation on, e.g. +0:
$ printf 'a\nb\nx %s+°C\nd\n' 60.1
a
b
x 60.1+°C
d
$ printf 'a\nb\nx %s+°C\nd\n' 60.1 |
awk 'NR == 3 {print $2+0}'
60.1
$ printf 'a\nb\nx %s+°C\nd\n' 60.1 |
awk -v max="70" 'NR == 3 {exit (($2+0)>max ? 0 : 1)}'; echo $?
1
$ printf 'a\nb\nx %s+°C\nd\n' 70.1 |
awk -v max="70" 'NR == 3 {exit (($2+0)>max ? 0 : 1)}'; echo $?
0
If that assumption is wrong then edit your question to show us the output of sensors cpu_thermal-virtual-0 | head -n 3 so we know what you're trying to parse.
Obviously pick whatever name you like for the function and change >= to > if you want the comparison to be greater than (as you say you want in your text) rather than greater than or equal to (as you have in your code with -ge).

Related

bash variable as a command: echo the command before execution and save the result to a variable

I am executing a chain of curl commands:
I need to echo the command before the execution.
Execute the command and save the result to a bash variable.
Get values from the result of the execution and execute the next curl with that values.
This is how it looks like:
# -----> step 1 <-----
URL="https://web.example.com:8444/hello.html"
CMD="curl \
--insecure \
--dump-header - \
\"$URL\""
echo $CMD && eval $CMD
OUT="<result of the curl command???>"
# Set-Cookie: JSESSIONID=5D5B29689EFE6987B6B17630E1F228AD; Path=/; Secure; HttpOnly
JSESSIONID=$(echo $OUT | grep JSESSIONID | awk '{ s = ""; for (i = 2; i <= NF; i++) s = s $i " "; print s }' | xargs)
# Location: https://web.example.com:8444/oauth2/authorization/openam
URL=$(echo $OUT | grep Location | awk '{print $2}')
# -----> step 2 <-----
CMD="curl \
--insecure \
--dump-header - \
--cookie \"$JSESSIONID\" \
\"$URL\""
echo $CMD && eval $CMD
OUT="<result of the curl command???>"
...
# -----> step 3 <-----
...
I only have a problem with the step 2: save the full result of the curl command to a variable in order to I can parse it.
I have tried it many different way, non of them works:
OUT="eval \$CMD"
OUT=\$$CMD
OUT=$($CMD)
...
What I missed?
For very basic commands, OUT=$($CMD) should work. The problem with this is, that strings stored in variables are processed differently than strings entered directly. For instance, echo "a" prints a, but var='"a"'; echo $a prints "a" (note the quotes). Because of that and other reasons, you shouldn't store commands in variables.
In bash, you can use arrays instead. By the way: The naming convention for regular variables is NOT ALLCAPS, as such names might accidentally collide with special variables. Also, you can probably drastically simplifiy your grep | awk | xargs.
url="https://web.example.com:8444/hello.html"
cmd=(curl --insecure --dump-header - "$url")
printf '%q ' "${cmd[#]}"; echo
out=$("${cmd[#]}")
# Set-Cookie: JSESSIONID=5D5B29689EFE6987B6B17630E1F228AD; Path=/; Secure; HttpOnly
jsessionid=$(awk '{$1=""; printf "%s%s", d, substr($0,2); d=FS}' <<< "$out")
# Location: https://web.example.com:8444/oauth2/authorization/openam
url=$(awk '/Location/ {print $2}' <<< "$out")
# -----> step 2 <-----
cmd=(curl --insecure --dump-header - --cookie "$jsessionid" "$url")
printf '%q ' "${cmd[#]}"; echo
out=$("${cmd[#]}")
# -----> step 3 <-----
...
If you have more steps than that, wrap the repeating part into a function, as suggested by Charles Duffy.
Easy Mode: Use set -x
Bash has a built-in feature, xtrace, which tells it to log every command to the file descriptor named in the variable BASH_XTRACEFD (by default, file descriptor 2, stderr).
#!/bin/bash
set -x
url="https://web.example.com:8444/hello.html"
output=$(curl \
--insecure \
--dump-header - \
"$url")
echo "Output of curl follows:"
echo "$output"
...will provide logs having the form of:
+ url=https://web.example.com:8444/hello.html
++ curl --insecure --dump-header - https://web.example.com:8444/hello.html
+ output=Whatever
+ echo 'Output of curl follows:'
+ echo Whatever
...where the + is based on the contents of the variable PS4, which can be modified to have more information. (I often use and suggest PS4=':${BASH_SOURCE}:$LINENO+' to put the source filename and line number in each logged line).
Doing It By Hand
If that's not acceptable, you can write a function.
log_and_run() {
{ printf '%q ' "$#"; echo; } >&2
"$#"
}
output=$(log_and_run curl --insecure --dump-header - "$url")
...will write your curl command line to stderr before storing its output in $output. Note when writing that output that you need to use quotes: echo "$output", not echo $output.
I guess OUT=$(eval $CMD) will do what you want.

How do I check the HTTP status code and also parse the payload

Imagine I have the following code in a bash script:
curl -s https://cat-fact.herokuapp.com/facts/random?animal=cat | jq .
Notice that I wish to display the payload of the response by passing it to jq.
Now suppose sometimes those curls sometimes return a 404, in such cases my script currently still succeeds so what I need to do is check the return code and exit 1 as appropriate (e.g. for a 404 or 503). I've googled around and found https://superuser.com/a/442395/722402 which suggests --write-out "%{http_code}" might be useful however that simply prints the http_code after printing the payload:
curl -s --write-out "%{http_code}" https://cat-fact.herokuapp.com/facts/random?animal=cat | jq .
$ curl -s --write-out "%{http_code}" https://cat-fact.herokuapp.com/facts/random?animal=cat | jq .
{
"_id": "591f98783b90f7150a19c1ab",
"__v": 0,
"text": "Cats and kittens should be acquired in pairs whenever possible as cat families interact best in pairs.",
"updatedAt": "2018-12-05T05:56:30.384Z",
"createdAt": "2018-01-04T01:10:54.673Z",
"deleted": false,
"type": "cat",
"source": "api",
"used": false
}
200
What I actually want to is still output the payload, but still be able to check the http status code and fail accordingly. I'm a bash noob so am having trouble figuring this out. Help please?
I'm using a Mac by the way, not sure if that matters or not (I'm vaguely aware that some commands work differently on Mac)
Update, I've pieced this together which sorta works. I think. Its not very elegant though, I'm looking for something better.
func() {
echo "${#:1:$#-1}";
}
response=$(curl -s --write-out "%{http_code}" https://cat-fact.herokuapp.com/facts/random?animal=cat | jq .)
http_code=$(echo $response | awk '{print $NF}')
func $response | jq .
if [ $http_code == "503" ]; then
echo "Exiting with error due to 503"
exit 1
elif [ $http_code == "404" ]; then
echo "Exiting with error due to 404"
exit 1
fi
What about this. It uses a temporary file. Seems me a bit complicated but it separates your content.
# copy/paste doesn't work with the following
curl -s --write-out \
"%{http_code}" https://cat-fact.herokuapp.com/facts/random?animal=cat | \
tee test.txt | \ # split output to file and stdout
sed -e 's-.*\}--' | \ # remove everything before last '}'
grep 200 && \ # try to find string 200, only in success next step is done
echo && \ # a new-line to juice-up the output
cat test.txt | \ #
sed 's-}.*$-}-' | \ # removes the last line with status
jq # formmat json
Here a copy/paste version
curl -s --write-out "%{http_code}" https://cat-fact.herokuapp.com/facts/random?animal=cat | tee test.txt | sed -e 's-.*\}--' | grep 200 && echo && cat test.txt | sed 's-}.*$-}-' | jq
This is my attempt. Hope it works for you too.
#!/bin/bash
result=$( curl -i -s 'https://cat-fact.herokuapp.com/facts/random?animal=cat' )
status=$( echo "$result" | grep -E '^HTTPS?/[1-9][.][1-9] [1-9][0-9][0-9]' | grep -o ' [1-9][0-9][0-9] ')
payload=$( echo "$result" | sed -n '/^\s*$/,//{/^\s*$/ !p}' )
echo "STATUS : $status"
echo "PAYLOAD : $payload"
Output
STATUS : 200
PAYLOAD : {"_id":"591f98803b90f7150a19c23f","__v":0,"text":"Cats can't taste sweets.","updatedAt":"2018-12-05T05:56:30.384Z","createdAt":"2018-01-04T01:10:54.673Z","deleted":false,"type":"cat","source":"api","used":false}
AWK version
payload=$( echo "$result" | awk '{ if( $0 ~ /^\s*$/ ){ c_p = 1 ; next; } if (c_p) { print $0} }' )
Regards!
EDIT : I have simplified this even more by using the -i flag
EDIT II : Removed empty line from payload
EDIT III : Included an awk method to extract the payload in case sed is problematic
Borrowing from here you can do:
#!/bin/bash
result=$(curl -s --write-out "%{http_code}" https://cat-fact.herokuapp.com/facts/random?animal=cat)
http_code="${result: -3}"
response="${result:0:${#result}-3}"
echo "Response code: " $http_code
echo "Response: "
echo $response | jq
Where
${result: -3} is the 3rd index starting from the right of the string till the end. This ${result: -3:3} also would work: Index -3 with length 3
${#result} gives us the length of the string
${result:0:${#result}-3} from the beginning of result to the end minus 3 from the http_status code
The site cat-fact.herokuapp.com isn't working now so I had to test it with another site

How to get dynamic NR parameter in AWK command at shell scripting

I have txt file like this:
1 a
2 b
3 c
I want to take these datas step by step for example first ı will get " 1 " and put it a varible and then get " a " put it in a varible and run a curl command
I mean first first row first column then fisrt row second column then second row first column .....
ı wrote a script in below but not working, it turns null varible for b and c
#!/bin/sh
for ((i=1;i<=4;i++)); do
echo $i
b=$(awk 'NR==$i { print $1}' a.txt)
c=$(awk 'NR==$i { print $2}' a.txt)
echo $b
echo $c
curl -X POST \
-H "X-netmera-api-key: xxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '[
{
"deviceToken" : "$b",
"platform" : "1",
"extId" : "$c"
}
]' \
https://xxxx.xxxxx.com/rest/3.0/xxxxx
done
Throw awk away; it isn't necessary here.
while read -r b c; do
curl -X POST \
-H "X-netmera-api-key: xxxxxxxxxx" \
-H "Content-Type: application/json" \
-d "[
{
\"deviceToken\" : \"$b\",
\"platform\" : \"1\",
\"extId\" : \"$c\"
}
]" \
https://xxxx.xxxxx.com/rest/3.0/xxxxx
done < a.txt
You shouldn't be trying to generate JSON dynamically like this, but that's an issue for another question.
You will need to pass variables to awk with -v and so:
b=$(awk -v varb=$i 'NR==varb { print $1}' a.txt)
Here we are setting the awk variable varb equal to the i variable of your bash loop.

Check return code in bash while capturing text

When running an ldapsearch we get a return code indicating success or failure. This way we can use an if statement to check success.
On failure when using debug it prints if the cert validation failed. How can I capture the output of the command while checking the sucess or failure of ldapsearch?
ldapIP=`nslookup corpadssl.glb.intel.com | awk '/^Address: / { print $2 }' | cut -d' ' -f2`
server=`nslookup $ldapIP | awk -F"= " '/name/{print $2}'`
ldap='ldapsearch -x -d8 -H "ldaps://$ldapIP" -b "dc=corp,dc=xxxxx,dc=com" -D "name#am.corp.com" -w "366676" (mailNickname=sdent)"'
while true; do
if [[ $ldap ]] <-- capture text output here ??
then
:
else
echo $server $ldapIP `date` >> fail.txt
fi
sleep 5
done
As #codeforester suggested, you can use $? to check the return code of the last command.
ldapIP=`nslookup corpadssl.glb.intel.com | awk '/^Address: / { print $2 }' | cut -d' ' -f2`
server=`nslookup $ldapIP | awk -F"= " '/name/{print $2}'`
while true; do
captured=$(ldapsearch -x -d8 -H "ldaps://$ldapIP" -b "dc=corp,dc=xxxxx,dc=com" -D "name#am.corp.com" -w "366676" "(mailNickname=sdent)")
if [ $? -eq 0 ]
then
echo "${captured}"
else
echo "$server $ldapIP `date`" >> fail.txt
fi
sleep 5
done
EDIT: at #rici suggestion (and because I forgot to do it)... ldap needs to be run before the if.
EDIT2: at #Charles Duffy suggestion (we will get there), we don't need to store the command in a variable.

unable to print the second variable data in the shell script

Esteemed colleagues,
I have written a small code where i'm storing some command output into two different variables and aspiring those two values to be printed under into different columns called "PreferredList IP's" & "DefaultServerList IP's".
there are Variables PLIST & DLIST,So, when i'm running the script i only see output under the first column and unable to get the data under second column.
This looks weird to me, i don't know where i'm doing mistake..please do correct me..
#!/bin/sh
set -f # to prevent filename expansion
printf "=%.0s" $(seq 1 50)
printf "\n"
printf "%-30s : %10s\n" "PreferredList IP's" "DefaultServerList IP's" # print header
printf "=%.0s" $(seq 1 50) # print separator
printf "\n" # print newline
PLIST="$(ldapsearch -h mylap -x -b "ou=profile,o=cadence.com" "cn=*" preferredserverlist -LLL | awk '/preferredserverlist: / {print $2}')"
DLIST="$(ldapsearch -h myldap -x -b "ou=profile,o=cadence.com" "cn=*" defaultserverlist -LLL | awk '/defaultserverlist: / { print $2 }')"
printf "%-30s : %10s\n" "$PLIST" "$DLIST"
RESULT: while using debug mode, I saw the problem is both the varibale output coming into first column.
======================================================
PreferredList IP's : DefaultServerList IP's
========================================================
123.18.8.15
123.18.8.16
192.10.167.9
192.10.167.8
123.18.8.16
10.218.88.38
Below is the ldapsearch command output sample:
dn: cn=india, ou=profile, o=cadence.com
preferredServerList: 123.18.8.15 123.18.8.16
defaultServerList: 123.18.8.16 123.18.8.15
dn: cn=japan, ou=profile, o=cadence.com
preferredServerList: 192.10.167.9 192.10.167.8
defaultServerList: 123.18.8.16 10.218.88.38
$ ldapsearch -h myldap -x -b "ou=profile,o=cadence.com" "cn=*" preferredserverlist -LLL | awk '/preferredserverlist: / {print $2}' | head -2
123.18.8.15
123.18.8.16
$ ldapsearch -h myldap -x -b "ou=profile,o=cadence.com" "cn=*" defaultserverlist -LLL | awk '/defaultserverlist: / { print $2 }' | head -2
123.18.8.16
10.218.88.38
It seems like the real problem you have is formatting your columns.
You have two list of IPs stored in PLIST and DLIST as strings, separated by newlines. When you type
printf "%-30s : %10s\n" "$PLIST" "$DLIST"
It will not automatically format those into columns for you.
You really need to change the way you're parsing your LDAP results. /bin/sh is really not inherently suited to this kind of output formatting.
If you have the option of using bash (version > 4), use mapfile and restructure your program like this:
#!/bin/bash
set -f # to prevent filename expansion
# Store the output of the ldapsearches in arrays using mapfile.
mapfile -t PLIST < <(ldapsearch -h mylap -x -b "ou=profile,o=cadence.com" "cn=*" preferredserverlist -LLL | awk '/preferredserverlist: / {print $2}')
mapfile -t DLIST < <(ldapsearch -h myldap -x -b "ou=profile,o=cadence.com" "cn=*" defaultserverlist -LLL | awk '/defaultserverlist: / { print $2 }')
# Count the number of elements in each array.
count_x=${#PLIST[#]}
count_y=${#DLIST[#]}
# Print the count for debugging.
echo $count_x
echo $count_y
# Find out which of the two arrays is larger in size, assuming that's a possibility, pick the length of the bigger one.
if [[ $count_x -lt $count_y ]]
then
count=$count_y
else
count=${count_x}
fi
printf "=%.0s" $(seq 1 50)
printf "\n"
printf "%-30s : %10s\n" "PreferredList IP's" "DefaultServerList IP's" # print header
printf "=%.0s" $(seq 1 50) # print separator
printf "\n" # print newline
# Use an index 0 < i <= count, to loop over the arrays simultaneously.
for i in $(seq $count);
do
printf "%-30s : %10s\n" "${PLIST[i-1]}" "${DLIST[i-1]}"
done
This uses bash's mapfile to store the output of the ldap search commands in an indexed array and prints it out in a formatted column.
As a test, I wrote this out and replaced your ldap commands with mock seq calls to generate numbers. Here's a sample run.

Resources