BASH Syntax Error - [: missing `]' - bash

I am new to Bash programming and probably being really silly.
Basically I am writing a piece of script that will ping an IP Address I pass in, it will take from it packets transmitted and return an error or a pass message depending on the number of packets lost.
However whenever I run the script from my terminal I keep getting message -
./ipGatewayCheck.sh: line 13: [: missing]'`
This is my code:
#!/bin/bash
healthy_status=0
warning_status=10
critical_status=100
for gateway in $#
do
RESULT=`ping -q -c 10 $gateway | grep 'packets transmitted' | awk '{print $6}' | tr -d "%"`
echo "$RESULT"
if [ $RESULT -eq $healthy_status ]; then
echo "No Issue - IP Address is pinging"
elif [ $RESULT -ge $warning_status && -le $critical_status ]; then
echo "Warning - Issue with packet loss on this IP Address"
elif [ $RESULT -eq $critical_status ]; then
echo "Critical - 100% packet loss on this IP Address"
fi
done
Any help would be greatly appreciated.

You need to use [[ and ]] in order to use && inside square brackets:
if [[ "$RESULT" -eq "$healthy_status" ]]; then
echo "No Issue - IP Address is pinging"
elif [[ "$RESULT" -ge "$warning_status" && "$RESULT" -le "$critical_status" ]]; then
echo "Warning - Issue with packet loss on this IP Address"
elif [[ "$RESULT" -eq "$critical_status" ]]; then
echo "Critical - 100% packet loss on this IP Address"
fi
Alternatively you can also use (( and )) in BASH:
if (( RESULT == healthy_status )); then
echo "No Issue - IP Address is pinging"
elif (( RESULT == warning_status && RESULT < critical_status )); then
echo "Warning - Issue with packet loss on this IP Address"
elif (( RESULT == critical_status )); then
echo "Critical - 100% packet loss on this IP Address"
fi

As diagnosed by anubhava in his answer, the problem is that the && operator terminates the test command leaving you with a [ without a matching ] and the error message you get.
There's an alternative fix — more traditional shell coding and portable to shells other than Bash.
If you wish to use [, you have to use either the -a conjunction (instead of &&), or use two separate tests:
elif [ "$RESULT" -ge $warning_status -a "$RESULT" -le "$critical_status" ]; then
elif [ "$RESULT" -ge $warning_status ] && [ "$RESULT" -le "$critical_status" ]; then
Note that I had to add the second "$RESULT"; I also enclosed the variables inside double quotes to make sure there are no mishaps.

Related

bash: check if two variables both do or do not exist (aka comparing results of comparisons)

I am writing a bash script that sometimes will use environment variables GIT_DIR and GIT_WORK_TREE. The bash script can only operate correctly if either both variables exist or neither exist. In case there's a technical difference, it makes no difference
This works, but there has to be a better way:
if [[ -z "${GIT_DIR}" ]]; then
_GIT_DIR_EXISTS=0
else
_GIT_DIR_EXISTS=1
fi
if [[ -z "${GIT_WORK_TREE}" ]]; then
_GIT_WORK_TREE_EXISTS=0
else
_GIT_WORK_TREE_EXISTS=1
fi
if [[ "${_GIT_DIR_EXISTS}" -ne "${_GIT_WORK_TREE_EXISTS}" ]]; then
echo "GIT_DIR is ${GIT_DIR}"
echo "GIT_WORK_TREE is ${GIT_WORK_TREE}"
echo "Both or none must exist"
exit 1
fi
I tried:
if [[ (-z "${GIT_DIR}") -ne (-z "${GIT_WORK_TREE}") ]]; then
But that gives this error:
bash: syntax error in conditional expression
bash: syntax error near '-ne'
I then resorted to trying semi-random things, with varying errors:
if [[ -z "${GIT_DIR}" -ne -z "${GIT_WORK_TREE}" ]]; then
if [[ [-z "${GIT_DIR}"] -ne [-z "${GIT_WORK_TREE}"] ]]; then
if [[ [[-z "${GIT_DIR}"]] -ne [[-z "${GIT_WORK_TREE}"]] ]]; then
if [[ -z "${GIT_DIR}" ]] ^ [[ -z "${GIT_WORK_TREE}" ]]; then
if { [[ -z "${GIT_DIR}" ]] } -ne { [[ -z "${GIT_WORK_TREE}" ]] }; then
if [[ (( -z "${GIT_DIR}" )) -ne (( -z "${GIT_WORK_TREE}" )) ]]; then
I tried:
if [[ $(test -z "${GIT_DIR}") -ne $(test -z "${GIT_WORK_TREE}") ]]; then
But realized that doesn't work because it's a sub-process, and they'd need to be exported. as Socowl comments, this compares the outputs of the test commands which output nothing, not their exit statuses.
I apologize if this is a duplicate. I've searched here and google for a while, and must not be using the right terminology.
How about this:
if [[ "${GIT_DIR:+set}" != "${GIT_WORK_TREE:+set}" ]]; then
echo "GIT_DIR is '${GIT_DIR}'"
echo "GIT_WORK_TREE is '${GIT_WORK_TREE}'"
echo "Both or none must exist"
exit 1
fi
Explanation: ${var:+value} is a variant of parameter expansion that gives "value" if var is set to a nonempty string, or the empty string if var is unset or empty. So if both vars are unset/empty, it becomes if [[ "" != "" ]]; then, and if they're both set it becomes if [[ "set" != "set" ]]; then etc.
BTW, if you want to test whether the variables are set at all (even if to the empty string), use ${var+value} (note the lack of colon). The bash manual lists the :+ version, but not the + version.

Bash script seemingly stops working after a few hours

So, this script runs for a few hours, but suddenly stops doing its job. According to top it's still running, but it no longer seems to do anything.
WANSTAT=1
LTESTAT=1
tail -f /var/log/messages | grep --line-buffered mwan3 | while read -r INPUT ; do
if [[ "$INPUT" == *"notice mwan3track[]: Interface wan (eth1) is offline" ]]; then
WANSTAT=0
echo "WAN offline"
elif [[ "$INPUT" == *"notice mwan3track[]: Interface lte (3g-lte) is offline" ]]; then
LTESTAT=0
echo "LTE offline"
elif [[ "$INPUT" == *"ifup interface wan (eth1)" ]]; then
WANSTAT=1
elif [[ "$INPUT" == *"ifup interface lte (3g-lte)" ]]; then
LTESTAT=1
fi
if [ $WANSTAT -eq 0 ] && [ $LTESTAT -eq 0 ]; then
echo "All red\n"
elif [ $WANSTAT -eq 0 ]; then
echo "WAN red, LTE green\n"
elif [ $LTESTAT -eq 0 ]; then
echo "LTE red, WAN green\n"
else
echo "All green\n"
fi
done
After a few hours, the logging system closes /var/log/messages, renames it, and opens a new file with the same name. tail -f, however, continues to watch the original file, which is no longer written to.
Use tail -F instead to ensure that you continue to watch the file named /var/log/messages, regardless of which file that actually is.

IP address calculator script

I need to write a Bash script that converts IP address from CIDR format to quad-style.
I must enter the IP address in the following style:
10.10.10.10/24
If I entered it in this style:
10.10.10.10 255.255.255.0
an error message should appear.
I tried this script:
#!/bin/bash
echo "enter you ip"
read ip
case $ip in
*.*.*.*/*)
b=`echo $ip | cut -d/ -f1`
a=`echo $ip | cut -d/ -f2`
if [ $a -eq 24 ];then
echo "$b 255.255.255.0"
elif [ $a -eq 25 ];then
echo "$b 255.255.255.128"
elif [ $a -eq 26 ];then
echo "$b 255.255.255.192"
elif [ $a -eq 27 ];then
echo "$b 255.255.255.224"
elif [ $a -eq 28 ];then
echo "$b 255.255.255.240"
elif [ $a -eq 29 ];then
echo "$b 255.255.255.248"
elif [ $a -eq 30 ];then
echo "$b 255.255.255.252"
elif [ $a -eq 31 ];then
echo "$b 255.255.255.254"
elif [ $a -eq 32 ];then
echo "$b 255.255.255.255"
fi
case $ip in
*.*.*.* *.*.*.*)
echo "enter a valid address"
esac
but I get an error
./ipcalculater2.sh: line 32: syntax error near unexpected token `...'
./ipcalculater2.sh: line 32: ` ... ...)'
What is wrong with my script?
Here's an example of four ways one might convert from CIDR to netmask notation in bash.
#!/usr/bin/env bash
if [[ "$1" != *.*.*.*/* ]]; then
echo "Usage: ${0##*/} ip.ad.dr.ess/bits" >&2
exit 1
fi
# separate our input into network and mask using IFS
IFS=/ read network bits <<<"$1"
# build a temporary variable $s that we'll use to build $bitmask
# for the first three variants...
read zeros <<< $(printf '%032s' 0)
s=${zeros//0/1}${zeros}
# convert the mask into a 32-digit binary number
bitmask=${s:$((32-bits)):32}
# Four different methods for generating the netmask...
# The first two use `bc` and `dc`. One is likely installed on your system.
read mask1 <<<$( dc -e "2i $(fold -w8 <<<"$bitmask " | paste -sdp -)" | paste -sd. - )
read mask2 <<<$( fold -w8 <<<"$bitmask" | paste - | bc -e 'ibase=2' | paste -sd. - )
# And if dc and bc are unavailable, or you prefer not to spawn subshells, or
# risk missed dependencies, you can do this in pure bash with a few more lines.
unset mask3
for ((i=0;i<4;i++)); do
mask3+="${mask3:+.}$((2#${bitmask:$((8*i)):8}))"
done
# And finally, instead of a loop, you can do the same thing with fancy math:
# This variant avoides the need for $bitmask, set above.
mask4="$(( 2**32 - (1 << (32-$bits)) ))"
mask4=$(( mask4/2**24 )).$(( mask4/2**16 %256 )).$(( mask4/2**8 % 256 )).$(( mask4 % 256 ))
# Print the results, obviously.
echo "network=$network"
echo "netlength=$bits"
echo "netmask via 'dc': $mask1"
echo "netmask via 'bc': $mask2"
echo "netmask via loop: $mask3"
echo "netmask via math: $mask4"
I've included code that works in each of dc and bc, since I can't predict which calculator will be available on your system. These calculators are used for base conversion. If you don't mind your script being a little longer, you can avoid spawning external tools (like fold and paste and the calculator) using the method that generates $netmask3.
Note that in the third case, this usage of bc depends on availability of a -e option which exists in *BSD. Use another variant if you're using GNU bc (i.e. you're in Linux.)
You can of course adjust the output so that it meets your needs, as well as trim the parts of the script you're not using.
Please check for this corrections:
#!/bin/bash
echo "enter you ip"
read ip
case $ip in
*.*.*.*/*)
b=`echo $ip | cut -d/ -f1`
a=`echo $ip | cut -d/ -f2`
if [ $a -eq 24 ];then
echo "$b 255.255.255.0"
elif [ $a -eq 25 ];then
echo "$b 255.255.255.128"
elif [ $a -eq 26 ];then
echo "$b 255.255.255.192"
elif [ $a -eq 27 ];then
echo "$b 255.255.255.224"
elif [ $a -eq 28 ];then
echo "$b 255.255.255.240"
elif [ $a -eq 29 ];then
echo "$b 255.255.255.248"
elif [ $a -eq 30 ];then
echo "$b 255.255.255.252"
elif [ $a -eq 31 ];then
echo "$b 255.255.255.254"
elif [ $a -eq 32 ];then
echo "$b 255.255.255.255"
fi
;;
*.*.*.*\ *.*.*.*)
echo "enter a valid address"
;;
esac

[: : integer expression expected

COUNTER=0
let COUNTER=COUNTER+1
count=`ssh -i /var/www/.ssh/id_rsa_root -o stricthostkeychecking=no $host $cmd`
count1=`echo $count | awk '{print $4}'`
printf "count1 : $count1\n"
result1=${count1/.*}
if [ "$result1" -ge "0" ]; then
echo $host
else
echo $host
exit
fi
If the value of $result1 is INTEGER and greater than zero, it'll goto IF loop (works fine for me)
But when it is not INTEGER, it is coming to else loop (which it is suppose to do) with the following error in the Output
line 55: [: : integer expression expected
but i dont want the above error in my output. I tried to use 2>/dev/null with this but no luck.
please help!
If you want to handle an empty result gracefully, check for it explicitly:
if [ -z "$result1" ]; then
: "ignoring empty string"
elif [ "$result1" -ge 0 ]; then
printf '%s\n' "$host"
else
printf '%s\n' "$host"
exit
fi
You could also check if result1 is a valid integer before making arithmetic comparisons:
function isNumber () {
[[ $1 =~ ^-?[0-9]+$ ]]
}
if ! isNumber "$result1"; then
echo "not a number"
elif [ "$result1" -ge "0" ]; then
echo "null or positive"
else
echo "negative"
fi
Change if [ "$result1" -ge "0" ]; then to
if (( result1 >= 0 )); then
This syntax won't throw any errors if result1 isn't defined (or empty) or happen to be a string somehow.

Assigning A default getway to a pc

I am writing a module to assign getway to pc.I have been using "route" command for it bt the comman is not working. The codes i have used are
echo type getway
read gwn
echo GETWAY: $gwn
function valid_gwn()
{
local gw=$1
local stat=1
if [[ $gwn =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]];
then
OIFS=$IFS
IFS='.'
gw=($gwn)
IFS=$OIFS
[[ ${gw[0]} -le 255 && ${gw[1]} -le 255 \
&& ${gw[2]} -le 255 && ${gw[3]} -le 255 ]]
stat=$?
fi
return $stat
}
if [[ $? -eq 0 ]] && valid_gwn
then
echo good;
else
echo bad;
fi
route add default gw $gwn eth0 metric 1
Please clarify the mistakes that i made.
I don't think I will have to compile anything from my end, as there is a good explanation given for the issue here
I guess, for debugging you should be echoing $gwn before setting the route.
Largely if nothing wrong happening while setting $gwn in script then this question could have been asked in Superuser of askubuntu.

Resources