Bash getopts doesn't show error for second option - bash

I want a script to take in two options, both are required. if I pass one in, the script doesn't print an error requesting you to pass in a second one.
-bash-4.2$ bash test.sh -b
Invalid option: b requires an argument
-bash-4.2$ bash test.sh -p
Invalid option: p requires an argument
-bash-4.2$ bash test.sh -b sdfsfd
-bash-4.2$ bash test.sh -p sdfsfd
-bash-4.2$ bash test.sh -b sdfsfd -s sfd
Invalid option: s
Code
showHelp()
{
cat << EOF
Find files in client's folder and upload to S3 bucket.
Usage: $(basename $0) [-p PATH_TO_SEARCH] [-b S3 bucket]
OPTIONS:
-h Show this help message
-p Path to search
-b S3 Bucket
EOF
exit 1
}
while getopts ":p:b:h" o; do
case "${o}" in
h)
showHelp
;;
p)
p=${OPTARG}
;;
b)
b=${OPTARG}
;;
\? )
echo "Invalid option: $OPTARG";;
: )
echo "Invalid option: ${OPTARG} requires an argument";;
esac
done
shift $((OPTIND-1))
if [ -z "${p}" ]; then
showHelp
fi
if [ -z "${b}" ]; then
showHelp
fi

If you want to ensure you get both options, you can use something like:
no_p=1
no_b=1
while getopts ":p:b:h" o; do
case "${o}" in
h)
showHelp
;;
p)
p=${OPTARG}
no_p=0
;;
b)
b=${OPTARG}
no_b=0
;;
\? )
echo "Invalid option: $OPTARG";;
: )
echo "Invalid option: ${OPTARG} requires an argument";;
esac
done
[[ $no_p -eq 1 ]] && echo "No -p provided" && exit 1
[[ $no_b -eq 1 ]] && echo "No -b provided" && exit 1

Related

Bash Shell Script: How can I restrict getopts so that it only takes one type of option?

I am using getopts and for the options I want to accept just one type of letter but it can be passed multiple times. Can't figure out how to do this but it should work the way ls -l does where that ls -lllllll and ls -l -l -l -l -l all return the same thing and it only runs once.
while getopts ":abc" opt; do
case "$opt" in
a) echo "a"
;;
b) echo "b"
;;
p) echo "c"
;;
?) echo "error"
;;
esac
done
so in this example, I want ./program.sh -a, ./program.sh -aaaaaaa (with any number of as), and ./program.sh -a -a -a -a to all return "a" just one time and then something like ./program.sh -ab or ./program.sh -abc or ./program.sh -a -c to return an error
Don't take action while parsing your options. Just record which options are seen, and take action afterwards.
while getopts ":abc" opt; do
case "$opt" in
a) A=1
;;
b) B=1
;;
c) C=1
;;
?) echo "error"
;;
esac
done
if ((A + B + C > 1)); then
printf 'Only one of -a, -b, -c should be used.\n' >&2
exit 1;
fi
[[ $A == 1 ]] && echo "a"
[[ $B == 1 ]] && echo "b"
[[ $C == 1 ]] && echo "c"
You have to write the logic yourself to handle multiple invocations of the same argument.
A=0
while getopts ":a" option; do
case "$option" in
a) [[ $A != 1 ]] && echo "a"
A=1
;;
*) echo "error"
;;
esac
done

The script does not run without flags

Wrote a script, the main task of which is to upload and download files.
#!/bin/bash
fileUpload() {
local filepath=$1
local transfer_path=$2
local url
url=$(curl --progress-bar --upload-file "$filepath" "https://transfer.sh/$transfer_path")
echo "$url"
}
printOutUpload() {
local filepath=$1
local url
echo "Uploading $filepath"
url=$(fileUpload "$filepath")
echo "Transfer File URL: $url"
}
fileDownload() {
local destination=$1
local url=$2
local file_name=$3
curl -# "https://transfer.sh/$url/$file_name" -o "$destination/$file_name"
}
printOutDownload() {
if [ $? -eq 0 ]; then
echo "Success!"
else
echo "Error: There was a problem downloading the file."
fi
}
while getopts "d:" opt; do
case $opt in
d)
printOutDownload
fileDownload "$2" "$3" "$4"
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
esac
done
With flag -d script download single file from the transfer:
./script -d ./tests HJkv1I test.txt
Without it should upload files:
./script test.txt test1.txt
One by one, each of these functions work correctly, but I can't get them to work in one script - If I add the -d flag, downloading is working, but upload not, its do nothing
Set a flag when processing the arguments. Then after the getopts loop, use an if statement to perform an upload or download depending on the flag.
direction=up
while getopts "d:" opt; do
case $opt in
d) direction=down
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
esac
done
shift (($OPTIND-1))
if [[ $direction == up ]]
then
printOutUpload
fileUpload "$#"
else
printOutDownload
fileDownload "$#"
fi

Pass wildcard to scp in a wrapper bash script

I am writing a bash wrapper for scp'ing into and from a certain host with a certain username, like:
johny#bonjour:~/bin$ cat scpphcl
#!/bin/bash
download=false
upload=false
local=""
remote=""
usage()
{
echo "Usage: $0 -d[-u] -l <LocalPath> -r <RemotePath>"
exit 1
}
while getopts "h?dul:r:" opt; do
case "$opt" in
h|\?)
usage
;;
d)
download=true
upload=false
;;
u)
download=false
upload=true
;;
l)
local=$OPTARG
;;
r)
remote=$OPTARG
;;
esac
done
if [[ -z $local || -z $remote ]]; then
echo "Need to provide local and remote path."
usage
fi
if $download; then
scp somebody#somehost:"$remote" $local
elif $upload; then
scp $local somebody#somehost:"$remote"
else
echo "Neither download nor upload?"
exit 1
fi
if [[ $? -ne 0 ]]; then
echo "Something wrong happened in the scp process."
exit 1
fi
exit 0
It works well with the usual filenames, but if there is any wildcard in the local filename field, it will not work right.
johny#bonjour:~/test$ scpphcl -u -l * -r /u/somebody/temp
Need to provide local and remote path.
Usage: /Users/johny/bin/scpphcl -d[-u] -l <LocalPath> -r <RemotePath>
There is a walkaround, using sinqle quotes around the local file argument if there is a wildcard in it:
johny#bonjour:~/test$ scpphcl -u -l '*' -r /u/somebody/temp
But even this walkaround will not work, if the command is issued outside the folder test:
johny#bonjour:~/test$ cd ..
johny#bonjour:~$ scpphcl -u -l 'test/*' -r /u/somebody/temp
This doesn't work and will hang in the scp process.
Any help in how to pass the wildcard in local filenames with the bash wrapper?
It's probably best not to require your users to quote wildcard patterns. I'd instead change the interface of your program to accept any number of local paths, after the option arguments:
echo "Usage: $0 [-d|-u] [-r <RemotePath>] <LocalPath>..."
When reading options, consume them with shift:
while getopts "h?dur:" opt; do
case "$opt" in
h|\?)
usage
exit 0
;;
d)
download=true
upload=false
;;
u)
download=false
upload=true
;;
r)
remote="$OPTARG"
;;
*)
usage >&2
exit 1
;;
esac
done
shift $((OPTIND-1))
Now the remaining positional arguments are the local filenames (and can be accessed with "$#" - note the all-important double-quotes there):
if test -z "$*" # no LocalPath arguments!
then usage >&2; exit 1
elif $download
then exec scp somebody#somehost:"$remote" "$#"
elif $upload
then exec scp "$#" somebody#somehost:"$remote"
fi

How to add debug/set -x is bash script as option for getopts

I want to pass the debug facility as an user-input in a bash script. Let's take the below code as a sample script.
#!/bin/bash
usage() { echo "Usage: $0 [-d <Integer>] [-m <String>]" 1>&2; exit 1; }
while getopts "hld:p:" o; do
case "${o}" in
d)
d=${OPTARG}
[[ $d =~ ^[0-9]+$ ]] || usage
;;
p)
p=${OPTARG}
[[ $p =~ [a-zA-Z] ]] || usage
;;
l) # to enable logging/debug -- set -x
l=${OPTARG}
;;
h|*)
usage
;;
esac
done
shift $((OPTIND-1))
if [ -z "${d}" ] || [ -z "${m}" ]; then
usage
fi
echo "d = ${d}"
echo "m = ${m}"
How can I add it in this?
getops argument, help message and case statement are not consistent:
getops argument d:h:p:, specifies options -d -h and -p (also h: means -h requires an argument)
help message, documents options -d and -m
case statement, handles options -d -p -l and -h ..
Adding l in getopts argument and removing : after using h
while getopts "d:hlp:" o; do
case "${o}" in
...
l) set -x
;;
...
esac
done

using getopts did not get the input value

I am running the below script, but it looks like the $filename or $srvname did not get the input value.
say for eg: ./test.sh -n abcd.net gives the output echo 'Filename or node name must be defined.'
it means that, the $srvname did not get the value "abcd.net", please advise am i doing anything wrong. ?
set -x
usage () {
echo "usage: $0 -n <nodename>"
echo "usage: $0 -f <filename>"
echo "usage: $0 -h <help>"
}
while getopts ":nfh:" opt; do
case "$opt" in
n) srvname="$OPTARG" ;;
f) filename="$OPTARG" ;;
h) # help
usage
exit 0
;;
:) echo "Error: -$OPTARG requires an argument"
usage
exit 1
;;
?) echo "Error: unknown option -$OPTARG"
usage
exit 1
;;
esac
done
function dosomecheck {
echo "do some checks"
}
if [ "$filename" != "" ] ; then
# read file
for x in `cat $filename` ; do
dosomecheck $x
done
fi
if [ "$srvname" != "" ] ; then
# read file
for x in $srvname ; do
dosomecheck $x
done
fi
Thanks in advance
Try doing:
while getopts ":n:f:h" opt;
because -n and -f takes argument while -h doesn't.

Resources