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
[...]
Related
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
I have a bash script which takes few command line args and a filename as inline parameter. I am not able to read the inline parameter.
sh test.sh -a a -b b -c c < pwd.txt
test.sh has
if [ $# = 0 ]
then
echo $USAGE >&2
exit $STATUS_ERROR_FAIL
fi
# Parse command line options.
while getopts a:b:c: OPT;
do
case "$OPT" in
a)
a="$OPTARG"
;;
b)
b="$OPTARG"
;;
c)
c="$OPTARG"
;;
\?)
# getopts issues an error message
echo $USAGE
exit $STATUS_ERROR_FAIL
;;
esac
done
shift $((OPTIND-1))
echo "1=$1"
your script is working fine, your error is
echo "1=$1"
if you want to see your parameter you should add an echo/print in your case
#!/bin/bash
if [ $# = 0 ]
then
echo $USAGE >&2
exit $STATUS_ERROR_FAIL
fi
# Parse command line options.
while getopts a:b:c: OPT;
do
case "$OPT" in
a)
a="${OPTARG}"
echo "a[$a]"
;;
b)
b="${OPTARG}"
echo "b[$b]"
;;
c)
c="${OPTARG}"
echo "c[$c]"
;;
\?)
# getopts issues an error message
echo $USAGE
exit $STATUS_ERROR_FAIL
;;
esac
done
shift $((OPTIND-1))
or
you can add the echo/print at the end of the script.. it's depend by your needs
output
[shell] ➤ ./t -a 1 -b 2 -c 3
a[1]
b[2]
c[3]
Regards
Claudio
This question already has an answer here:
How to support both short and long options at the same time in bash? [duplicate]
(1 answer)
Closed 7 years ago.
I have a function with a few parameters. For example:
makeUser{
login
email
password}
I want to make flags like -l|--login, -e|--email and -p|--password but I don't know how.
The sample for it should look like the code below:
./script.sh --mode makeUser --login test --email test#test.com -p testxx
How can I achieve that result? I know only how to make it with getopts (short flags).
Should it look like code below?
while true ; do
case "$1" in
-m|--mode)
makeUser)
case "$2" in
-l|--login)
makeUser "$OPTARG"
case "$3" in
-e|--email)
makeUser "$OPTARG"
case "$4" in
-p|--password)
makeUser "$OPTARG"
exit $?
esac ;;
exit $?
esac ;;
exit $?
esac ;;
makeProject)...
makeSite)...
exit $?
esac ;;
done
Using while and shift makes a clean solution for getopt-alike behaviour in bash:
while [ $# -gt 0 ]; do
case "$1" in
-h|"-?"|--help)
shift
echo "usage: $0 [-v] [--mode MODE] [-l login] [-e email] [...]"
exit 0
;;
--mode)
MODE=$2
shift; shift;
;;
-l|--login)
LOGIN=$2
shift; shift;
;;
-e|--email)
EMAIL=$2
shift; shift;
;;
-v|--verbose)
VERBOSE=1
shift;
;;
*)
echo "Error: unknown option '$1'"
exit 1
esac
done
# example function makeUser
makeUser()
{
login=$1
email=$2
echo "function makeUser with login=${login} and email=${email}"
}
if [ "$MODE" == "makeUser" ]; then
makeUser $LOGIN $EMAIL # ... and so on
fi
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
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