Flexible number of parameters in a bash script - bash

I am using the following kind of bash code. I store some information within log file which name is defined in the bash script.
LOGNAME="/tmp/ETH"
LOG_FILE="${LOGNAME}.log"
function exit_error()
{
case "$1" in
100 )
echo "Bad arguments supplied - Enter help"
echo "Bad arguments supplied - Enter help" >> "${LOG_FILE}"
;;
101 )
echo "Illegal number of parameters"
echo "Illegal number of parameters" >> "${LOG_FILE}"
;;
* )
;;
esac
exit 1;
}
function current_status()
{
INT_STATUS=$(cat /sys/class/net/eth1/operstate)
echo "status : $INT_STATUS"
echo "status : $INT_STATUS" >> "${LOG_FILE}"
}
function connect_eth()
{
...
}
...
case "$1" in
current_status )
if [ "$#" -ne 1 ]
then
exit_error 101
else
current_status
fi
;;
connect_eth )
if [ "$#" -ne 1 ]
then
exit_error 101
else
connect_eth
fi
;;
read_MAC_addr )
if [ "$#" -ne 1 ]
then
exit_error 101
else
read_MAC_addr
fi
;;
read_IP_addr )
if [ "$#" -ne 1 ]
then
exit_error 101
else
read_IP_addr
fi
;;
* )
exit_error 100
;;
esac
exit 0;
I would like to modify the script in order to use the specified log name if no other log name is specified as last parameter. However, I would like to keep my "exit_error 101" in switch case which is based on the number of parameters passed to the script. Is there a way to do that ? Because I can not modify the $# variable.

It should be possible. Do something like this:
CMD="$1"
shift
# use provided logname or set to default if not found
LOGNAME="${1:-/tmp/ETH}
shift
LOGFILE="${LOGNAME}.log"
# now, since we shifted, you just have to check for $# -eq 0 to
# be sure there are no params left.
... your function definitions here ...
# exit 101 if there are some parameters left
if [ $# -ne 0 ]; then
exit_error 101
fi
case "$CMD" in
current_status)
current_status
;;
...
*)
exit_error 100
;;
esac
If you want more flexibility, you can always use getopts and named parameters. It is usually much easier to maintain.
And, if I were you, I would also centralize error handling before the case statement to avoir repeating the same check everywhere.

Related

Bash subcommands with arguments

I have a script in bash as such:
#!/usr/bin/env bash
set -e
if [[ "$#" == 0 ]]; then
printhelp
exit 1
fi
# process options
while [[ "$1" != "" ]]; do
case "$1" in
-n | --name)
shift
_NAME="$1"
;;
-i | --id)
shift
_ID="$1"
;;
-h | --help)
printhelp
exit 1
;;
*)
printhelp
exit 1
;;
esac
shift
done
This works fine, but I want to add some "actions" that will take the above params. Eg. usage will be:
./run.sh create --name foo --id 1234
./run.sh delete --id 1234
I am not able to figure out the right syntax, and I am unable to phrase this requirement into appropriate words to be able to search.
For sub-command you can handle it this way:
function main(){
if (( ${#} == 0 )); then
main_help 0;
fi
case ${1} in
help | version | encrypt | decrypt )
$1 "${#:2}";
;;
* )
echo "unknown command: $1";
main_help 1;
exit 1;
;;
esac
}
main "$#";
Then wrap each sub-command is a function. And inside each function you will have isolated options and parsing it separately.
For example:
function decrypt(){
if [[ ${#} == 0 ]]; then
decrypt_help;
fi
local __filename='';
local __salt='';
local __anchor=false;
local error_message='';
while [ ${#} -gt 0 ]; do
error_message="Error: a value is needed for '$1'";
case $1 in
-f | --file )
__filename=${2:?$error_message}
shift 2;
;;
-s | --salt )
__salt=${2:?$error_message}
shift 2;
;;
-a | --anchor )
__anchor=${2:?$error_message}
shift 2;
;;
* )
echo "unknown option $1";
break;
;;
esac
done
echo filename: ${__filename:-empty};
echo salt: ${__salt:-empty};
echo anchor: $__anchor;
exit 0;
}
Here is a full version bash-CLI-template I have used in my projects
demo ;)
Sounds like you want something like:
create() {
# actiony stuff here
}
ACTION=$1 ; shift
# put all your argument parsing here
$ACTION # call
However, since different actions probably have different arguments, I'd probably do it differently...
create() {
# argument parsing for create
# then do your create stuff
}
ACTION=$1 ; shift
$ACTION "$#"
This will pass all your arguments to your subfunction, which can then parse its own arguments.

Nagios, my own custom plugin doesnt work

I'm trying to add my own custom check to nagios.
I have successfully created a bash script, that is executable from nagios user.
The problem is that this script works fine from linux command line, but it's return the unknown status if runned by nagios ( returned in web gui ).
#!/bin/sh
while getopts ":q:c:w:h:u:p" optname
do
case "$optname" in "q") query=$OPTARG
;;
"c") CIRT=$OPTARG
;;
"w") WARN=$OPTARG
;;
"u") user=$OPTARG
;;
"p") pswd=$OPTARG
;;
"h") echo "Useage: check_SQLplus_query -u user -p password -w warning value -c cirtical value"
exit
;;
"?") echo "Unknown option $OPTARG"
exit
;;
":") echo "No argument value for option $OPTARG"
exit
;;
*) # Should not occur
echo "Unknown error while processing options"
exit
;;
esac
done
RETVAL=`sqlplus -s USER/PASSWORD#HOST:1521/DBNAME<<EOF
SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF
$query;
EXIT;
EOF`
if [ "$RETVAL" -le "$CIRT" ]
then
echo "OK - $RETVAL"
exit 0
elif [ "$RETVAL" -gt "$CIRT" ] && [ "$RETVAL" -le "$WARN" ]
then
echo "WARNING - $RETVAL"
exit 1
elif [ "$RETVAL" -gt "$WARN" ]
then
echo "CRITICAL - $RETVAL"
exit 2
else
echo "UNKNOWN - $RETVAL"
exit 3
fi
Actually the user, password are "coded" inside script, even if I've already set the getopt to make this script more flexible.
The idea is simple, use sqlplus to get a simple query that return only a number ( like the number of the row like my case ).
Bash line to start the script:
/usr/lib64/nagios/plugins/check_SQLplus_queryPrimavera.sh -q "select count(*) from ADMUSER_PM.REFRDEL" -w 6000000 -c 8000000

how to make sure that N+1 argument is present when Nth argument is equal to "--check"

I am trying to write code to check if any argument (on position N) is equal to "--check" and, if its true, require that next argument (position N+1) is present. Otherwise, exit.
How can i achieve that?
i am trying sth like this but it doesnt seem to work:
i am reiterating arguments and if "--check" is found then setting FLAG to 1 which triggers another conditional check for nextArg:
FLAG=0
for i in "$#"; do
if [ $FLAG == 1 ] ; then
nextARG="$i"
FLAG=0
fi
if [ "$i" == "--check" ] ; then
FLAG=1
fi
done
if [ ! -e $nextARG ] ; then
echo "nextARG not found"
exit 0
fi
I would go with getopts. The link shows an example how you could check for your missing parameter.
You could use a form like this. I use it as a general approach when parsing arguments. And I find it less confusing than using getopts.
while [[ $# -gt 0 ]]; do
case "$1" in
--option)
# do something
;;
--option-with-arg)
case "$2" in)
check_pattern)
# valid
my_opt_arg=$2
;;
*)
# invalid
echo "Invalid argument to $1: $2"
exit 1
;;
esac
# Or
if [[ $# -ge 2 && $2 == check_pattern ]]; then
my_opt_arg=$2
else
echo "Invalid argument to $1: $2"
exit 1
fi
shift
;;
*)
# If we don't have default argument types like files. If that is the case we could do other checks as well.
echo "Invalid argument: $1"
# Or
case $1 in
/*)
# It's a file.
FILES+=("$1")
;;
*)
# Invalid.
echo "Invalid argument: $1"
exit 1
;;
esac
esac
shift
done

parsing parameters in shell script

I have a myscript.sh which starts like this:
#!/usr/bin/env bash
set -e
usage(){
echo "Show Usage ... Blah blah"
exit 1
}
if [ $# = 0 ]; then
usage;
fi
while true; do
case "$1" in
-l | --build-lib ) BUILD_LIB=true;
--other-option ) OTHER_OPTION=$2; shift; shift;;
-h | --help ) usage; shift;;
* ) break ;;
esac
done
# I do my thing here ....
echo "Do my thing"
I am not sure if this is the best way to parse the parameters but so far I have a problem. I am not correctly breaking/failing when the user passes wrong or unknown parameters. How can I address this correctly?
for example I want to avoid calls like:
$ ./myscript.sh unknownParameter
You need to exit when an incorrect option is given, not just break out of the loop. Easiest way is to call your usage function.
while [ $# -gt 0 ]; do
case "$1" in
-l | --build-lib ) BUILD_LIB=tru ;;
--xcode-dev-path ) XCODE_DEV_PATH=${2%/}; shift ;;
-h | --help ) usage;;
* ) usage ;;
esac
shift
done

Why do I get an "unexpected operator" error for my condition in Bash?

I got the code for monitoring apache. The name of the file is test.sh. I changed the code a bit.
What I was looking for is, when I do:
./test.sh -H localhost -wr 2 -cr 5 -arg cpu_load
It should test apache for its cpu_load, i.e., I tried to control monitoring apache with my -arg parameter, but that doesn't seem to be working.
When I run this:
./test.sh -H localhost -wr 2 -cr 5 -arg cpu_load
I get the error :
./test.sh: 282: [: -ge: unexpected operator
./test.sh: 286: [: -ge: unexpected operator
Here is some part of the code:
#!/bin/sh
while test -n "$1"; do
case "$1" in
--help|-h)
print_help
exit $ST_UK
;;
--version|-v)
print_version $PROGNAME $VERSION
exit $ST_UK
;;
--hostname|-H)
hostname=$2
shift
;;
--port|-P)
port=$2
shift
;;
--timeout|-t)
timeout=$2
shift
;;
--remote-server|-R)
remote_srv=1
;;
--binary_path|-b)
path_binary=$2
shift
;;
--pid_path|-p)
path_pid=$2
shift
;;
--pid_name|-n)
name_pid=$2
shift
;;
--status-page|-s)
status_page=$2
shift
;;
--secure|-S)
secure=1
;;
--warning-req|-wr)
warn_req=$2
shift
;;
--critical-req|-cr)
crit_req=$2
shift
;;
--userargument|-arg)
user_arg=$3
shift
;;
*)
echo "Unknown argument: $1"
print_help
exit $ST_UK
;;
esac
shift
done
#other codes
if [ ${wclvls_req} = 1 ]
then
if [ ${user_arg} -ge ${warn_req} -a ${user_arg} -lt ${crit_req} ]
then
echo "WARNING - ${output} | ${perfdata}"
exit $ST_WR
elif [ ${user_arg} -ge ${crit_req} ]
then
echo "CRITICAL - ${output} | ${perfdata}"
exit $ST_CR
else
echo "OK - ${output} | ${perfdata}"
exit $ST_OK
fi
else
echo "OK - ${output} | ${perfdata}"
exit $ST_OK
fi
fi
Where am I making the mistake?
One of your variables ( user_arg, warn_req etc ) in the if condition might be empty.
Better way to write that is with quoting the variables as (which may fail in your case if you want to compare as integers):
if [ "${user_arg}" -ge "${warn_req}" -a "${user_arg}" -lt "${crit_req}" ]
Or another way is to specify the default values so that if variable is null or undefined if won't fail as below.
if [ ${user_arg:-0} -ge ${warn_req:-0} -a ${user_arg:-0} -lt ${crit_req:-0} ]
If you don't need POSIX compatibility, you can use bash's arithmetic evaluation command instead:
if (( user_arg >= 0 && user_arg < crit_req )); then
Unset variables will be implicitly treated as 0-valued, so using default value expansion is unnecessary.

Resources