I want my bash script to only accept one letter in the switch, with nothing after it.
Here's my code (foo.sh):
#!/bin/bash
while getopts ":ap" jeep;do
case $jeep in
a)
echo 'Hi Brad'
;;
b)
echo 'Hi Eddy'
;;
else)
echo 'Hi whoever'
\?)
echo 'Bad input' >&2
exit 1
;;
esac
done
I want it to act like this:
$ foo.sh -a
Hi Brad
$ foo.sh -b
Hi Eddy
$ foo.sh
Hi whoever
$ foo.sh -r
Bad input
$ foo.sh -ab
Bad input
$ foo.sh -ba
Bad input
Instead, it runs both a and b (shown below) if I use -ab, which I don't want it to.
$ foo.sh -ab
Hi Brad
Hi Eddy
Also, else) doesn't work like I'd like it to. I want it to say Hi whoever if no switches are typed.
How can I fix this?
getopt does not have a way of specifying mutually exclusive flags, so you have to create your own. The simplest way to do this is probably something like this:
while getopts ":ab" jeep
do
case $jeep in
a)
if [ -n "${run-}" ]
then
exit 1
fi
echo 'Hi Brad'
run=1
;;
b)
if [ -n "${run-}" ]
then
exit 1
fi
echo 'Hi Eddy'
run=1
;;
\?)
if [ -n "${run-}" ]
then
exit 1
fi
echo 'Hi whoever'
run=1
;;
esac
done
Related
I'm trying to make a getopt command such that when I pass the "-ab" parameter to a script,
that script will treat -ab as a single parameter.
#!/bin/sh
args=`getopt "ab":fc:d $*`
set -- $args
for i in $args
do
case "$i" in
-ab) shift;echo "You typed ab $1.";shift;;
-c) shift;echo "You typed a c $1";shift;;
esac
done
However, this does not seem to work. Can anyone offer any assistance?
getopt doesn't support what you are looking for. You can either use single-letter (-a) or long options (--long). Something like -ab is treated the same way as -a b: as option a with argument b. Note that long options are prefixed by two dashes.
i was struggling with this for long - then i got into reading about getopt and getopts
single char options and long options .
I had similar requirement where i needed to have number of multichar input arguments.
so , i came up with this - it worked in my case - hope this helps you
function show_help {
echo "usage: $BASH_SOURCE --input1 <input1> --input2 <input2> --input3 <input3>"
echo " --input1 - is input 1 ."
echo " --input2 - is input 2 ."
echo " --input3 - is input 3 ."
}
# Read command line options
ARGUMENT_LIST=(
"input1"
"input2"
"input3"
)
# read arguments
opts=$(getopt \
--longoptions "$(printf "%s:," "${ARGUMENT_LIST[#]}")" \
--name "$(basename "$0")" \
--options "" \
-- "$#"
)
echo $opts
eval set --$opts
while true; do
case "$1" in
h)
show_help
exit 0
;;
--input1)
shift
empId=$1
;;
--input2)
shift
fromDate=$1
;;
--input3)
shift
toDate=$1
;;
--)
shift
break
;;
esac
shift
done
Note - I have added help function as per my requirement, you can remove it if not needed
That's not the unix way, though some do it e.g. java -cp classpath.
Hack: instead of -ab arg, have -b arg and a dummy option -a.
That way, -ab arg does what you want. (-b arg will too; hopefully that's not a bug, but a shortcut feature...).
The only change is your line:
-ab) shift;echo "You typed ab $1.";shift;;
becomes
-b) shift;echo "You typed ab $1.";shift;;
GNU getopt have --alternative option
-a, --alternative
Allow long options to start with a single '-'.
Example:
#!/usr/bin/env bash
SOPT='a:b'
LOPT='ab:'
OPTS=$(getopt -q -a \
--options ${SOPT} \
--longoptions ${LOPT} \
--name "$(basename "$0")" \
-- "$#"
)
if [[ $? > 0 ]]; then
exit 2
fi
A=
B=false
AB=
eval set -- $OPTS
while [[ $# > 0 ]]; do
case ${1} in
-a) A=$2 && shift ;;
-b) B=true ;;
--ab) AB=$2 && shift ;;
--) ;;
*) ;;
esac
shift
done
printf "Params:\n A=%s\n B=%s\n AB=%s\n" "${A}" "${B}" "${AB}"
$ ./test.sh -a aaa -b -ab=test
Params:
A=aaa
B=true
AB=test
getopt supports long format. You can search SO for such examples.
See here, for example
Hi all I think I am going insane. I have the following at the beginning of a bash script:
#!/bin/bash
while getopts i:o:c:p:g: flag
do
case "${flag}" in
g) genome=${OPTARG};;
p) poscol=${OPTARG};;
c) chromcol=${OPTARG};;
i) inf=${OPTARG};;
o) outf=${OPTARG};;
esac
done
echo ""
echo "This should be a list of the arguments:"
echo $inf
echo $outf
echo $poscol
echo $chromcol
echo $genome
echo "-----------------------"
Yet when I run a test of the code, eg:
bash Code.sh -i test.txt -o test.txt -c 0 -p 1 -g testgenome
bash returns:
This should be a list of the arguments:
-----------------------
I literally just copied this section of code from a script where the arguments work fine. Does anyone have any idea what could be going on here? Thanks in advance.
In the following script:
#!/usr/bin/env bash
func_usage ()
{
cat <<EOF \
USAGE: ${0} \
EOF
}
## Defining_Version
version=1.0
## Defining_Input
options=$(getopt -o "t:" -l "h,help,v,version,taxonomy:" -a -- "$#")
eval set -- "$options"
while true;do
case $1 in
-h|--h|-help|--help)
func_usage
exit 0
;;
-v|--v|-version|--version)
echo $version
;;
-t|--t|-taxonomy|--taxonomy)
echo "Option t = $2 ";
Taxonomy_ID=$2
echo $Taxonomy_ID
shift
;;
--)
shift
break;;
esac
shift
done
## Defining Taxonomy Default Value (in case is not provided)
TaxonomyID=${Taxonomy_ID:=9606};
echo $TaxonomyID
exit 0
The commands:
./script.sh -v
./script.sh --v
./script.sh -version
./script.sh --version
Work as expected. But what I do not understand is why the commands:
./script.sh -ver
./script.sh --ver
work at all. An equivalent unexpected behavior is also observed for the commands:
./script.sh -tax 22
./script.sh --tax 22
I would be grateful to get an explanation and/or a way to correct this unexpected behavior.
Note that getopt is an external utility unrelated to Bash.
what I do not understand is why the commands: .. work at all.
Because getopt was designed to support it, there is no other explanation. From man getopt:
[...] Long options may be abbreviated, as long as the abbreviation is not ambiguous.
Unambiguous abbreviations of long options are converted to long options.
Based on the comments I have received, specially from #CharlesDuffy, I have modified my code to what I believe is a more robust and compatible version. Importantly, the code below addresses the pitfalls of the original code
#!/usr/bin/env bash
func_usage ()
{
cat <<EOF
USAGE: ${0}
EOF
## Defining_Version
version=1.0
## Defining_Input
while true;do
case $1 in
-h|--h|-help|--help|-\?|--\?)
func_usage
exit 0
;;
-v|--v|-version|--version)
echo $version
;;
-t|--t|-taxonomy|--taxonomy)
echo "Option t = $2 ";
Taxonomy_ID=$2
echo $Taxonomy_ID
shift
;;
--)
shift
break;;
-?*)
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
;;
*)
break
esac
shift
done
TaxonomyID=${Taxonomy_ID:=9606};
echo $TaxonomyID
exit 0
The code above behaves as expected in that the commands:
./script -tax 22
Gives the warning:
WARN: Unknown option (ignored): -tax
9606
As expected
I have a shell script that I use to launch some ROS launcher file (not that important for my question). My requirement is to use some arguments and therefore I am using getopts.
Basically, I am getting one file or a few, playing them with ROS and simultaneously recording all the files back to a single file. After that, if I have provided an argument -r or -c, I would like to run two additional operations (reindex and compress) on the recorded file. But it is required that the other process are done, before I can run the -r and -c. I am using the wait keyword, but I am not sure I really understand the flow. In other words, the playing and recording should be done and only then -r and -c should be run if provided as arguments.
Second question is related to how do I get or pass the same file that was outputted to these two functions (reindex and compress)?
So my desired format is:
./myscript -i file1 -i file2 -o /home/output -r -c
#!/bin/bash
OUTPUT_FILE_NAME="output_$(date +%Y.%m.%d--%H_%M_%S)"
usage="
$(basename "$0") [-i] [-o] [-r] [-c] [-h] -- to be done"
# Reset is necessary if getopts was used previously in the script.
OPTIND=1
while getopts ":i:orch:" opt; do
case $opt in
i ) echo "INPUT file - argument = $OPTARG"; input_files+=($OPTARG) ;;
o ) echo "OUTPUT dir - argument = $OPTARG"; output_dir=($OPTARG) ;;
r ) echo "REINDEX"; reindex ;;
c ) echo "COMPRESS"; compress ;;
h ) echo "$usage"
graceful_exit ;;
* ) echo "$usage"
exit 1
esac
done
# Shift off the options
shift $((OPTIND-1))
roslaunch myLauncher.launch &
echo "Number of loaded files: ${#input_files[#]}"
echo -n "FILES are:"
rosbag play ${input_files[#]} &
rosbag record -o $output_dir/$OUTPUT_FILE_NAME -a &
wait
function reindex{
rosbag reindex $output_dir/$OUTPUT_FILE_NAME
}
function compress{
rosbag reindex $output_dir/$OUTPUT_FILE_NAME
}
Thank you in advance!
You're very close to where you need to be — and using getopts puts you firmly on the correct track, too. Note whether or not you need to reindex or compress in the option parsing loop. Then, after the music has been played and the output file written, run the code from the functions if you need to:
#!/bin/bash
OUTPUT_FILE_NAME="output_$(date +%Y.%m.%d--%H_%M_%S)"
usage="$(basename "$0") [-i file] [-o file] [-r] [-c] [-h]"
input_files=() # Empty list of files/tracks
output_dir="." # Default output directory
r_flag="no" # No reindex by default
c_flag="no" # No compress by default
while getopts ":i:orch:" opt; do
case $opt in
(i) echo "INPUT file - argument = $OPTARG"; input_files+=("$OPTARG");;
(o) echo "OUTPUT dir - argument = $OPTARG"; output_dir="$OPTARG";;
(r) echo "REINDEX"; r_flag="yes";;
(c) echo "COMPRESS"; c_flag="yes";;
(h) echo "$usage" >&2; exit 0;;
(*) echo "$usage" >&2; exit 1;;
esac
done
# Shift off the options
shift $((OPTIND-1))
if [ $# != 0 ] || [ "${#input_files[#]}" = 0 ] ||
then
echo "$usage" >&2
exit 1
fi
roslaunch myLauncher.launch &
echo "Number of loaded files: ${#input_files[#]}"
echo -n "FILES are:"
rosbag play "${input_files[#]}" &
rosbag record -o "$output_dir/$OUTPUT_FILE_NAME" -a &
wait
if [ "$r_flag" = "yes" ]
then
rosbag reindex "$output_dir/$OUTPUT_FILE_NAME"
fi
if [ "$c_flag" = "yes" ]
then
rosbag compress "$output_dir/$OUTPUT_FILE_NAME"
fi
I didn't keep the functions since they didn't provide any value in the rewritten code.
I just ran into a problem while I was trying to assign a value to a variable inside a case statement, here is my code:
#!/bin/bash
while getopts ":m:n:::" opt; do
case $opt in
n)
echo "-n was triggered, Parameter: $OPTARG " >&2
case $OPTARG in
t)
echo threads
r=threads
;;
p)
echo processes
r="something"
;;
esac
;;
m)
echo "-m was triggered, Parameter: $OPTARG" >&2
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
echo $r
echo No thread/processes: $2 P/T: $4 IF: $5 OF: $6
I'd like to use the variable $r later, but I can't. When I try to print it using echo (as it is in my script), it does not return a thing.
I've been trying to spot my mistake but I couldn't.
There is a similar post that suggested to remove blank spaces before and after the =, but as you can see, there are no blank spaces in mine.
Here is what I get from the console when I run it:
$ ./friendfind -n 2 -m p IN OUT
-n was triggered, Parameter: 2
-m was triggered, Parameter: p
No thread/processes: 2 P/T: p IF: IN OF: OUT
The purpose of the script was to run a c file with the option to run it with threads or processes, so it asks for the number of processes/threads you want to use, if you want to tu use processes or threads and the input and output file.
I think you were aiming at something like this:
#!/bin/bash
# Print usage message:
usage() {
echo "Usage: $0 [-n N] [-t|-p] INPUT OUTPUT" >> /dev/stdout
}
# Set default values
n_threads=1
use_threads=1
while getopts "n:pth" opt; do
case $opt in
n) n_threads=$OPTARG;;
t) use_threads=1;;
p) use_threads=0;;
h) usage; exit 0;;
*) usage; exit 1;;
esac
done
# Get rid of scanned options
shift $((OPTIND-1))
if (($# != 2)); then usage; exit 1; fi
if ((use_threads)); then
echo "Using $n_threads threads. IF: $1; OF: $2"
# ...
else
echo "Using $n_threads processes. IF: $1; OF: $2"
# ...
fi
Here's some example invocations, including a couple of errors:
$ ./ff -p foo bar
Using 1 processes. IF: foo; OF: bar
$ ./ff foo bar
Using 1 threads. IF: foo; OF: bar
$ ./ff -n 7 foo bar
Using 7 threads. IF: foo; OF: bar
$ ./ff -n 7 -p foo bar
Using 7 processes. IF: foo; OF: bar
$ ./ff -p -n7 foo bar
Using 7 processes. IF: foo; OF: bar
$ ./ff -q -n7 foo bar
./ff: illegal option -- q
Usage: ./ff [-n N] [-t|-p] INPUT OUTPUT
# Note: The error message here could be more informative.
# Exercise left for the reader
$ ./ff -n 7 foo
Usage: ./ff [-n N] [-t|-p] INPUT OUTPUT