Using getopts and ${1} together - bash

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

Related

Is there a way in bash script to have an option to give an argument but it shouldn't a must?

I have a scenario where i would like to assign an option a default value but a user can decide to give it another argument:
Here is an example
check_param() {
for arg in "$#"; do
shift
case "$arg" in
"--force") set -- "$#" "-f" ;;
"--type") set -- "$#" "-t" ;;
"--help") set -- "$#" "-h" ;;
"--"*) echo "Unknown parameter: " $arg; show_help; exit 1 ;;
*) set -- "$#" "$arg"
esac
done
# Standard Variables
force=0
type="daily"
OPTIND=1
while getopts "hft:v" opt
do
case "$opt" in
"f") force=1 ;;
"t") type=${OPTARG} ;;
"h") show_help; exit 0 ;;
"?") show_help; exit 1 ;;
esac
done
shift $(expr $OPTIND - 1) # remove options from positional parameters
From the above example, i would like when the user gives the parameter -t without any argument to apply the default value which is daily , and the user can also use parameter -t with any other argument and that will be checked later in code.
The problem is now the parameter -t must be given an argument due to the colon, but i kinda need for it to do both, with or without argument.
Thanks in advance for any explanations or links to any article that can help.
So according to a suggestion i got Here is the test result
check_param() {
## Standard Variablen der Parameter
force=0
type="daily.0"
## Break down the options in command lines for easy parsing
## -l is to accept the long options too
args=$(getopt -o hft::v -l force,type::,help -- "$#")
eval set -- "$args"
## Debugging mechanism
echo ${args}
echo "Number of parameters $#"
echo "first parameter $1"
echo "Second parameter $2"
echo "third parameter $3"
while (($#)); do
case "$1" in
-f|--force) force=1; ;;
-t|--type) type="${2:-${type}}"; shift; ;;
-h|--help) show_help; exit 0; ;;
--) shift; break; ;;
*) echo "Unbekannter Parameter"; exit 1; ;;
esac
shift
done
echo ${type}
}
check_param $#
echo ${type}
The output:
sh scriptsh -t patch.0
-t '' -- 'patch.0'
Number of parameters 4
first parameter -t
Second parameter
third parameter --
daily.0
daily.0
It still didn't assign the value patch to the variable type
Is there a way in bash script to have an option to give an argument but it shouldn't a must?
Yes, there is a way.
getopts does not supports optional arguments. So... you can:
roll your own bash library for parsing arguments or
use another tool that has support for optional arguments.
A common tool is getopt that should be available on any linux.
args=$(getopt -o hft::v -l force,type::,help -- "$#")
eval set -- "$args"
while (($#)); do
case "$1" in
-f|--force) force=1; ;;
-t|--type) type="${2:-default_value}"; shift; ;;
-h|--help) echo "THis is help"; exit; ;;
--) shift; break; ;;
*) echo "Error parsgin arguments"; exit 1; ;;
esac
shift
done
getopt handles long arguments and reorders arguments, so you can ./prog file1 -t opt and ./prog -t opt file1 with same result.

Shell script with parameters?

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

How can I accept long arguments using getopts in Bash?

I'm trying to have my getops function run with multiple flags and arguments but instead of short (-f style) flag, I want to accept a long one (--flag style). For example:
if [ $# -lt 1 ]; then
usage >&2
exit 1
else
while $1 "hf:" opt; do
case $opt in
h)
echo "Here is the help menu:"
usage
;;
f)
ls -l $OPTARG >&2
;;
\?)
echo "Invalid option: -$OPTARG" >&2
;;
:)
echo "Option -$OPTARG requires an argument" >&2
exit 1
;;
esac
done
fi
I would like the -h and -f to be --help and --file respectively.
How do I do that?
getopt will do this for you. It handles short options, long options, options with and without arguments, -- to end option parsing, and more.
Boilerplate usage looks like:
options=$(getopt -o hf: -l help,file: -n "$0" -- "$#") || exit
eval set -- "$options"
while [[ $1 != -- ]]; do
case $1 in
-h|--help) echo "help!"; shift 1;;
-f|--file) echo "file! $2"; shift 2;;
*) echo "bad option: $1" >&2; exit 1;;
esac
done
shift
# Process non-option arguments.
for arg; do
echo "arg! $arg"
done

Script with non-option and option arguments

I'm trying to handle both optional and mandatory parameter to my bash script. I have following script:
while getopts "a:x:" opt; do
case $opt in
a) echo "option a set: $OPTARG" ;;
x) echo "option x set: $OPTARG" ;;
\?) echo "Invalid option: -$OPTARG" >&2; exit 1;;
esac
done
shift $((OPTIND-1))
echo "mandatory argument $1"
echo "mandatory argument2 $2"
Everything looks ok when I run my script using following command:
./script.sh -a optionA -x optionX mandatory1 mandatory2
But when I mix this params:
./script.sh mandatory1 mandatory2 -a optionA -x optionX
It doesn't... How to make it works for all combination of parameters?
You can iterate between both kinds of argument, I think.
I think this does what you want, and allows you to use -- to prevent the following arguments being interpreted as options.
mandatory=()
while [ $# -gt 0 ] && [ "$1" != "--" ]; do
while getopts "a:x:" opt; do
case $opt in
a) echo "option a set: $OPTARG" ;;
x) echo "option x set: $OPTARG" ;;
\?) echo "Invalid option: -$OPTARG" >&2; exit 1;;
esac
done
shift $((OPTIND-1))
while [ $# -gt 0 ] && ! [[ "$1" =~ ^- ]]; do
mandatory=("${mandatory[#]}" "$1")
shift
done
done
if [ "$1" == "--" ]; then
shift
mandatory=("${mandatory[#]}" "$#")
fi
echo "mandatory argument ${mandatory[0]}"
echo "mandatory argument2 ${mandatory[1]}"
Basically, the idea is to consume all the options with getopt, then consume all the non-options manually, then look for more options with getopt again.
To make it work I had to unset OPTIND after the shift $((OPTIND-1)):
[...]
shift $((OPTIND-1))
unset OPTIND
[...]

shell get opt is catputre $1 as argument

FILE_LIST=$1
MOVE=0
while getopts "m" OPT; do
case $OPT in
m) MOVE=1 ;;
M) MOVE=1 ;;
*) echo "Invalid parameter." >&2; exit 1 ;;
esac
done
echo $MOVE
echo $FILE_LIST
I will pass optional argument ( -m/-M) and file list .
test.sh -m a.txt
its display 1 -m , but i am looking for 1 a.txt
Supost if test.sh a.xt
it should be diplsay 0 and a.txt
You need to shift the arguments.
MOVE=0
while getopts "mM" OPT; do
case $OPT in
M|m) MOVE=1
shift;;
*) echo "Invalid parameter." >&2; exit 1 ;;
esac
done
echo $MOVE
FILE_LIST=$1
echo $FILE_LIST
You can also combine m and M into one case.
If I understand right, you want the syntax for running the script to be something like:
./scriptname [-mM] firstfile [secondfile ...]
If this is correct, none of the other answers quite work; here's how I'd do it:
#!/bin/bash
# Parse command options
MOVE=0
while getopts "mM" OPT; do
case "$OPT" in
m|M) MOVE=1 ;;
*) echo "Invalid option." >&2; exit 1 ;;
esac
done
shift $(( OPTIND-1 )) # Remove options from the argument list
# Parse command arguments
if [[ $# -eq 0 ]]; then
echo "No files specified." >&2
exit 1
fi
FILE_LIST=( "$#" ) # Use an array in case of spaces in filenames
# Some examples of things to do with the results:
# Work with the specified files individually:
for FILE in "${FILE_LIST[#]}"; do
chmod g+w "$FILE"
done
# Work with the specified files as a group:
if (( MOVE == 1 )); then
mv "${FILE_LIST[#]}" "$DEST_DIR"
else
cp "${FILE_LIST[#]}" "$DEST_DIR"
fi
I do not exactly know what you want but Here are some code examples:
First example assumes that the filelist is given always after the -m option
while getopts "m:" OPT
do
case $OPT in
m)
echo "option m"
FILE_LIST = $OPTARG
;;
*)
echo "error"
;;
esac
done
echo $FILE_LIST
Or a different approach with a filelist not related to the -m option
while getopts "m:" OPT
do
case $OPT in
m)
echo "option m"
MOVE = 1
;;
*)
echo "error"
;;
esac
done
shift $(($OPTIND - 1))
FILE_LIST = $1
echo $FILE_LIST
Hope this suits your needs
You have to use $OPTARG value for this. Notice m:. The colon specifies that there are arguments passed to -m
#!/bin/bash
MOVE=0
while getopts "m:M:" OPT; do
case $OPT in
m|M) MOVE=1
FILE_LIST="$FILE_LIST $OPTARG"
;;
*) echo "Invalid parameter." >&2; exit 1 ;;
esac
done
shift $(( OPTIND-1 ))
[[ $MOVE != 1 ]] && FILE_LIST=$1
echo $MOVE
echo $FILE_LIST

Resources