Sorry for my newbie question, I'm confused!
I want to make a bash script for some reasons.
I need to pass some arguments when running the script.
for example:
script.sh build --with-test --without-test2 --with-test3
script.sh config
script.sh config --with-test3 --without-test2
script.sh config --add this is a test
build or config is required and also other parameters are optional and the order of using argument is not important.
I wrote this code:
if [[ $# -lt 1 ]]; then
printf "build or config parameter is missing"
exit;
fi
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
build )
mode=1
shift
shift
;;
config )
mode=0
shift
shift
;;
-wt2 | --without-test2 )
wt2=0
shift
shift
;;
-wt3 | --with-test3 )
wt3=1
shift
shift
;;
-wt0 | --with-test )
wt0=1
shift
shift
;;
-add | --additional )
additional_command=$2
shift
shift
;;
-h | --help )
help
shift
shift
exit
;;
*)
echo "Missing parameter."
shift
shift
exit
;;
esac
done
But my code does not work properly, the script will run even without build and config, I cannot figure out how can I write my if statements
The use of shift twice is a mistake. You can confirm that config AND/OR build has been specified by checking the value of mode. There also looks to be an issue with additional command.
Consider moving the shift statement.
I've added some debug to your program to see what the results are:
$ script.sh --with-test --without-test2 --with-test3
additional_command=
mode=
wt0=1
wt2=
wt3=1
As you can see, the wt2 is not set, even though --without-test2 was specified.
Here's how you can fix things up a little:
Remove the shifts in the case (except for special processing)
Review the mode parameter to see if it is set ( test -z )
Code follows:
#!/bin/bash
if [[ $# -lt 1 ]]; then
printf "build or config parameter is missing"
exit;
fi
while [[ $# -gt 0 ]]
do
key="$1"
shift
case $key in
build )
mode=1
;;
config )
mode=0
;;
-wt2 | --without-test2 )
wt2=0
;;
-wt3 | --with-test3 )
wt3=1
;;
-wt0 | --with-test )
wt0=1
;;
-add | --additional )
# Get the rest of the parameters, right?
additional_command=$*
shift $# # Shift to "deplete" the rest of the params
;;
-h | --help )
help
# No need to shift if you are exiting
exit
;;
*)
echo "Missing parameter."
# No need to shift if you are exiting
exit
;;
esac
done
#Here's a little debug code that you can remove or just comment out with :
#Change first line to : cat <<EOF if you want to comment out.
cat <<EOF
additional_command=$additional_command
mode=$mode
wt0=$wt0
wt2=$wt2
wt3=$wt3
EOF
if [ -z "$mode" ] ; then
echo "Missing config or build parameter"
fi
Related
I have a shell script that takes parameters, below is the code..
Right now it will only accept parameters if passed if called like this: script.sh --mode=load (or -m=load)
Is there a way to modify this so that it can be called with or without the "=" sign, so that I can call: script.sh --mode load (or -m load)
Ideally needs to work in pure bash as I don't have access to install additional tools, etc.
for i in "$#"
do
case $i in
-m=*|--mode=*)
MODE="${i#*=}"
if [[ $MODE =~ ^(dump|load)$ ]]; then
echo "" > /dev/null
else
bark "Invalid --mode set, set this to dump or load.";
exit 1
fi
;;
-p=*|--db-path=*)
DBPATH="${i#*=}"
;;
-d=*|--dump-dir=*)
DUMPDIR="${i#*=}"
;;
-l=*|--list-file=*)
TABLES="${i#*=}"
# check if file exists on disk
if [ -e $TABLES ]
then
echo "" >> /dev/null
else
bark "Table file not found!";
exit 1
fi
;;
-t=*|--tenant-name=*)
TENANT="${i#*=}"
# check if tenant is correct
if [[ $TENANT =~ ^($TENANT_LIST)$ ]]; then
echo "" >> /dev/null
else
bark "Tenant name does not match, aborting.";
exit 1
fi
;;
-s|--shared)
SHARED=YES
;;
*) usage # unknown option
;;
esac
done
My bash version:
bash --version
GNU bash, version 4.3.22(1)-release (powerpc-ibm-aix5.1.0.0)
Loop on $#. When $1 is "-m", do a shift. So in the next loop $1 will now be the argument to the -m option.
script.sh --mode load
# FIRST LOOP
$# is "--mode load"
$1 is "--mode"
shift
# SECOND LOOP
$# is "load"
$1 is "load"
This is also useful if you can specify many arguments instead of just one like you have right now. Error checking should be done to validate your argument values, and if a user did script.sh --mode with no other argument.
Don't reinvent the wheel.
If you're OK with just 1 character options, use the bash builtin getopts
#!/bin/bash
while getopts :m:p:d:l:t:s opt; do
case $opt in
m) mode=$OPTARG ;;
p) dbpath=$OPTARG ;;
d) dumpdir=$OPTARG ;;
l) tables=$OPTARG
# test file existence
;;
t) tenant=$OPTARG
# test tenant
;;
s) shared=YES ;;
:) echo "Missing argument for option -$OPTARG" >&2
exit 2
;;
*) echo "Invalid option -$OPTARG" >&2
exit 2
;;
esac
done
shift $((OPTIND - 1))
cat << SHOW_VARS
I have:
mode=$mode
dbpath=$dbpath
dumpdir=$dumpdir
tables=$tables
tenant=$tenant
shared=$shared
rest of args=$*
SHOW_VARS
Otherwise, you may be able to use the external getopt program to help parse your args. I don't have an AIX box to test on, so YMMV
tempargs=$(
getopt \
-o m:d:l:t:s \
--long mode:,db-path:,dump-dir:,list-file:,tenant-name:,shared \
-- "$#"
)
if [[ $? -ne 0 ]]; then echo "Error..." >&2; exit 2; fi
eval set -- "$tempargs"
while true; do
case $1 in
-m|--mode) mode=$2; shift 2;;
-p|--db-path) dbpath=$2; shift 2;;
-d|--dump-dir) dumpdir=$2; shift 2;;
-l|--list-file) tables=$2
# test file existence
shift 2
;;
-t|--tenant-name) tenant=$2
# test tenant
shift 2
;;
-s|--shared) shared=YES; shift;;
--) shift; break ;;
*) echo "Error..." >&2; exit 2 ;;
esac
done
I'm reading my command line parameters using getopt, and I'm reading a configuration file using .:
test.sh:
#!/bin/bash
set -- `getopt C:a:b:c: "$#"`
C="default.cfg"
. $C
while [ $# -gt 0 ]; do
case "$1" in
-a) cfg1="$2"; shift;;
-b) cfg2="$2"; shift;;
-c) cfg3="$2"; shift;;
-C) C="$2"; #you'll see what this is for later
shift;;
--) shift;
break;;
-*) echo "invalid option";
exit 1;;
*) break;;
esac
shift
done
echo "cfg1 = $cfg1"
echo "cfg2 = $cfg2"
echo "cfg3 = $cfg3"
exit 0
default.cfg::
cfg1=hello
cfg2=there
cfg3=friend
This all works as expected:
$ ./test.sh
cfg1 = hello
cfg2 = there
cfg3 = friend
$ ./test.sh -b optional
cfg1 = hello
cfg2 = optional
cfg3 = friend
This issue is I want configurations to be prioritized in the following manner:
options given on the command line
options defined in the config file defined by the -C option
options defined in the default config file
So if I have this:
test.cfg:
cfg1=custom_file_1
cfg2=custom_file_2
I want to get this:
$ ./test.sh -b command_line -C test.cfg
cfg1 = custom_file_1
cfg2 = command_line
cfg3 = friend
I just can't figure out how to load the default config file, then search the options for -C, then load the custom config file, overwriting the default, then search the command line parameters AGAIN and overwrite the configs again. I'm pretty new to shell scripting, so forgive me if I'm missing something obvious.
You can preprocess the arguments and pull out the value you're looking for:
#!/bin/bash
args=$(getopt C:a:b:c: "$#")
eval set -- $args
conf="default.cfg"
source "$conf"
# pre-process the arguments and see if we can find -C
found=0
for opt in "$#"; do
if [[ $found -eq 1 ]] && [[ -f "$opt" ]]; then
source "$opt"
break
fi
if [[ "$opt" == "-C" ]]; then
found=1
fi
done
while [ $# -gt 0 ]; do
case "$1" in
-a) cfg1="$2"; shift;;
-b) cfg2="$2"; shift;;
-c) cfg3="$2"; shift;;
-C) shift;; #don't do anything with this
--) shift;
break;;
-*) echo "invalid option";
exit 1;;
*) break;;
esac
shift
done
echo "cfg1 = $cfg1"
echo "cfg2 = $cfg2"
echo "cfg3 = $cfg3"
exit 0
To overwrite variables, try to replace :
-C) C="$2";
with :
-C) . "$2";
And invoke it with :
./test.sh -C test.cfg -a command_line1 -b command_line2
Update :
For options in any order, you can try this :
C="default.cfg"
. $C
while getopts C:a:b:c: OPTION
do
case $OPTION in
a) cfg1_override=$OPTARG;;
b) cfg2_override=$OPTARG;;
c) cfg3_override=$OPTARG ;;
C) . $OPTARG;;
-) break;;
-*) echo "invalid option";
exit 1;;
*) break;;
esac
done
shift $(($OPTIND - 1))
cfg1="${cfg1_override-${cfg1}}"
cfg2="${cfg2_override-${cfg2}}"
cfg3="${cfg3_override-${cfg3}}"
echo "cfg1 = $cfg1"
echo "cfg2 = $cfg2"
echo "cfg3 = $cfg3"
exit 0
Based on Is it possible to specify the order getopts conditions are executed?
First source default.cfg.
Than scan your options for a -C option. Handle this one when found.
Finally use getopts and skip -C when you find it during getopts.
I'm trying to write a script that can use both ${1} and getopts options simultaneously. I would like it to work using the usage line:
./test_script test -a
to print:
test
-a was triggered!
I've tried
echo ${1};
while getopts "c:a" opt; do
case $opt in
a)
echo "-a was triggered!" >&2
;;
\?)
echo "Invalid option: -$OPTARG" >&2
;;
esac
done
Which is not able to give me access to both ${1} and detect that the -a option was used simultaneously. Is there a way to use both of these? I'd like to avoid turning the test string into another getopts option.
You could use the shift when getopts exit.
For example:
while [ $# -gt 0 ] ; do
while getopts "c:a" opt ; do
case $opt in
# YOUR OPTIONS
esac
done
OTHER_VALUE=$1
shift
done
PS: usually I don't use getopts, but I prefer to parse the args by myself as following:
while [ $# -gt 0 ] ; do
case "$1" in
'-a' | '--along' )
echo '-a was triggered' ;;
'-b' | '--blong' )
echo '-b was trigger with arg ' $2 ;
shift ;; # One extra shift for the argumnent $2
* )
echo 'Unknown value (maybe test)' ;;
esac
shift
done
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
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.