Using getopts in Bash - bash

I want to use getopts in a Bash script as follows:
while getopts ":hXX:h" opt; do
case ${opt} in
hXX ) Usage
;;
h ) echo "You pressed Hey"
;;
\? ) echo "Usage: cmd [-h] [-p]"
;;
esac
done
The idea behind is that I want to have two flags -h or --help for allowing user to be able to use HELP in order to be guided how to use the script and another second flag which starts with h but its like -hxx where x is whatever.
How can I distinguish these two since even when I press --hxx flag it automatically executes help flag. I think the order of presenting them in getopt has nothing to do with this.

The 'external' getopt program (NOT the bash built in getopts) has support for '--longoptions'. It can be used as a pre-procssor to the command line options, making it possible to consume long options with the bash built-in getopt (or to other programs that do not support long options).
See: Using getopts to process long and short command line options for more details.
#! /bin/bash
TEMP=$(getopt -l help -- h "$#")
eval set -- "$TEMP"
while getopts h-: opt ; do
case "$opt" in
h) echo "single" ;;
-) case "$OPTARG" in
-help) echo "Double" ;;
*) echo "Multi: $OPTARG" ;;
esac ;;
*) echo "ERROR: $opt" ;;
esac
done

Related

Changing the value of the variable from another shell script

I have a shell script that has a on/off switch inside. I programmed the Housekeeping.sh to execute this certain line of code if the value is at 1 and don't execute it if it's at 0.
The following is the code on my Housekeeping.sh:
ARCHIVE_SWITCH=1
if [[ $ARCHIVE_SWITCH -eq 1 ]]; then
sqlplus ${BPS_SCHEMA}/${DB_PASSWORD}#${ORACLE_SID} #${BATCH_HOME}/sql/switch_archive.sql
fi
Now I want to create another shell script file that I'll execute to automatically change the variable ARCHIVE_SWITCHequals to 0 everytime I execute this script.
Is there any other way that I can change the value of the variable ARCHIVE_SWITCH from another shell script file that I'll execute manually?
I'd use an option to the script:
bash housekeeping.sh # default is off
bash housekeeping.sh -a # archive switch is on
#!/usr/bin/env bash
archive_switch=0
while getopts :a opt; do
case $opt
a) archive_switch=1 ;;
*) echo "unknown option -$opt" >&2 ;;
esac
done
shift $((OPTIND-1))
if ((archive_switch)); then
sqlplus ...
fi

Execute local script on remote host by passing remote host parameters on command line together with script arguments

is anybody aware if there is a syntax to pass a remote host parameters (user and IP/hostname) together with script arguments on local host and make it execute on the remote host?
I'm not meaning like this: $ ssh user#remoteServer "bash -s" -- < /path/script.ssh -a X -b Y
I want instead for the script to be able to be passed like this: $/path/script.ssh user#remoteServer -a X -b Y
But I'm not sure how to achieve, in the script, this kind of behaviour:
[...] script [...]
connect to user#remoteServer
[...] execute the script code (on the remote host) [...]
end of script
Any suggestion? Do I need to work this from another way instead?
EDIT
I've managed to make the script execute something after it connects via SSH, but I'm a bit as for why some commands are executed before they are passed to the remote host terminal; my code looks like this at the moment:
while getopts 'ha:u:d:s:w:c:' OPT; do
case $OPT in
a) host=$OPTARG;;
u) user=$OPTARG ;;
d) device=$OPTARG ;;
s) sensor=$OPTARG ;;
w) warn_thresh=$OPTARG ;;
c) crit_thresh=$OPTARG ;;
h) print_help
*) printf "Wrong option or value\n"
print_help
esac
done
shift $(($OPTIND - 1))
# Check if host is reachable
if (( $# )); then
ssh ${user}#${host} < $0
# Check for sensor program or file
case $device in
linux) do things
raspberry) do things
amlogic) do things
esac
# Read temperature information
case $device in
linux) do things
raspberry) do things
amlogic) do things
esac
# Check for errors
if (())
then
# Temperature above critical threshold
# Check for warnings
elif (())
then
# Temperature above warning threshold
fi
# Produce Nagios output
printf [......]
fi
The script seemingly runs without issue, but I get no output.
A simplistic example -
if (( $# )) # if there are arguments
then ssh "$1" < $0 # connect to the first and execute this script there
else whoami # on the remote, there will be no args...
uname -n # if remote needs arguments, change the test condition
date # these statements can be as complex as needed
fi
My example script just takes a target system login as its first argument.
Run it with no args it outputs the data for the current system; use a login, it runs there.
If you have password-less logins with authorized keys it's very smooth, otherwise it will prompt you.
Just parse your arguments and behave accordingly. :)
If you need arguments on the remote, use a more complex test to decide which branch to take...
Edit 2
I repeat: If you need arguments on the remote, use a more complex test to decide which branch to take...
while getopts 'ha:u:d:s:w:c:' OPT; do
case $OPT in
a) host=$OPTARG;;
u) user=$OPTARG ;;
d) device=$OPTARG ;;
s) sensor=$OPTARG ;;
w) warn_thresh=$OPTARG ;;
c) crit_thresh=$OPTARG ;;
h) print_help
*) printf "Wrong option or value\n"
print_help
esac
done
shift $(($OPTIND - 1))
# handoff to remote host
if [[ -n "$host" ]]
then scp "${user}#${host}:/tmp/" "$0"
ssh "${user}#${host}" "/tmp/${0##*/} -d $device -s $sensor -w $warn_thresh -c $crit_thresh"
exit $?
fi
# if it gets here, we're ON the remote host, so code accordingly
# Check for sensor program or file
case $device in
linux) do things
raspberry) do things
amlogic) do things
esac
# Read temperature information
case $device in
linux) do things
raspberry) do things
amlogic) do things
esac
# Check for errors
if (())
then
# Temperature above critical threshold
# Check for warnings
elif (())
then
# Temperature above warning threshold
fi
# Produce Nagios output
printf [......]
fi

Can a shell script flag have optional arguments if parsing with getopts?

I have a script that I want to run in three ways:
Without a flag -- ./script.sh
With a flag but no parameter -- ./script.sh -u
With a flag that takes a parameter -- ./script.sh -u username
Is there a way to do this?
After reading some guides (examples here and here) it doesn't seem like this is a possibility, especially if I want to use getopts.
Can I do this with getopts or will I need to parse my options another way? My goal is to continue using getopts if I can.
The non-getopts example in BashFAQ #35 can cover the use case:
user_set=0 # 1 if any -u is given
user= # set to specific string for -u, if provided
while :; do
case $1 in
-u=*) user_set=1; user=${1#*=} ;;
-u) user_set=1
if [ -n "$2" ]; then
user=$2
shift
fi ;;
--) shift; break ;;
*) break ;;
esac
shift
done

Testing whether or not a flag has an argument

My typical setup for parsing command-line options is:
CONF=""
INPUT=""
while getopts ":c:i:" FLAG; do
case $FLAG in
i) INPUT=$OPTARG;;
c) CONF=$OPTARG;;
\?) echo -e "\nInvalid option: -$OPTARG"
usage;;
:) echo -e "Option -$OPTARG requires an argument."
usage;;
esac
done
if [ "$#" -eq 0 ]; then
usage
fi
I'm looking for a way to catch when a valid flag is provided but no argument is - for example:
./Script.sh -c -i
Returns usage. I was under the impression that this line:
:) echo -e "Option -$OPTARG requires an argument."
Handled this however when running the script as above using flags without arguments, the usage function is not firing nor is the echo.
What am I doing wrong?
If you invoke your script with either ./script.sh -c -i -c or ./script.sh -c, both will show the error message Option -c requires an argument.
However, when invoking ./script.sh -c -i, you are passing value "-i" for the -c argument, so that at the end of arguments parsing, you end up with CONF=-c and INPUT not set.

Parsing mixed arguments in a script bash

I need to implement a script called with mixed (optional and non-optional) arguments for example -
./scriptfile -m "(argument of -m)" file1 -p file2 -u "(argument of -u)"
in a random order. I've read a lot about the getopts builtin command, but I think it doesn't solve my problem. I can't change the order of arguments, so I don't understand how I can read the arguments one by one.
Someone have any ideas?
You should really give a try to getopts, it is designed for that purpose :
Ex :
#!/bin/bash
while getopts ":a:x:" opt; do
case $opt in
a)
echo "-a was triggered with $OPTARG" >&2
;;
x)
echo "-x was triggered with $OPTARG" >&2
;;
\?)
echo "Invalid option: -$OPTARG" >&2
;;
esac
done
Running the script with different switches ordering :
$ bash /tmp/l.sh -a foo -x bar
-a was triggered with foo
-x was triggered with bar
$ bash /tmp/l.sh -x bar -a foo
-x was triggered with bar
-a was triggered with foo
As you can see, there's no problem to change the order of the switches
See http://wiki.bash-hackers.org/howto/getopts_tutorial
Consider using Python and its excellent built-in library argparse. It will support almost any reasonable and conventional command line options, and with less hassle than bash (which is, strangely, a fairly poor language when it comes to command line argument processing).

Resources