Combining multiple options into one single option (Getopts) - linux

Due to my lack of thorough understanding using getopts, the title is definitely vague :0. I am currently writing a bash script and I would like to add an option that outputs the other options within the case statement in getopts. For the sake of scaling, I have shortened the program.
#!/bin/bash
while getopts :abc opt
do
case $opt in
a)
echo "Hello"
;;
b)
echo "Goodbye"
c)
:ab #****I WANT -c TO OUTPUT THE RESULTS OF a and b************
;;
esac
done
As you can see in option c, I would like this particular option (-c) to put out both the results of -a and -b. Is there a way to go about this by simply making c call on option a and b?

you can introduce functions to reduce duplications, something like this:
#!/bin/bash
do_a() {
echo "Hello"
}
do_b() {
echo "Goodbye"
}
while getopts :abc opt
do
case $opt in
a)
do_a
;;
b)
do_b
;;
c)
do_a
do_b
;;
esac
done

If you are using a recent version of Bash, instead of terminating case clauses with ;; you could use bash specific ;;& with multiple patterns:
#!/bin/bash
while getopts :abc opt
do
case $opt in
a|c)
echo "Hello"
;;&
b|c)
echo "Goodbye"
;;&
esac
done
And:
$ bash script.bash -a
Hello
$ bash script.bash -c
Hello
Goodbye
Using ‘;;&’ in place of ‘;;’ causes the shell to test the patterns in the next clause, if any, and execute any associated command-list on a successful match.

Related

Using same getopts arguments for different options

I created a bash script and i need help with getopts
Each options have arguments :
while getopts ":a:b:c:" opt
do
case $opt in
a) echo "$OPTARG";;
b) echo "$OPTARG";;
c) echo "$OPTARG";;
/?) echo "wrong option $OPTARG";;
esac
done
If i want to use the same argument, i need to type -option arg for each options
./script.sh -a hello -b hello -c hello
hello
hello
hello
I would like to be able to use the script like a command, typing options all together when i have the same argument :
./script.sh -abc hello
Unfortunately bc is now the argument :
bc
Does anyone could help me please ?
Regards,

getopts: How to accept arguments that aren't tied to an option in my script? [duplicate]

This question already has answers here:
How do I parse command line arguments in Bash?
(40 answers)
Closed 6 years ago.
I'm writing a Bash script that has the following usage:
ci-badge -t|-a [-b branch] [-m] [-d description] [-l [link] ] repo
Examples:
$ ci-badge -t foo/bar
$ ci-badge -ab dev foo/bar -m
$ ci-badge quux/bar -md 'Hello, world.'
More samples can be found here on GitHub. Anyway, I'm wondering exactly how to implement argument parsing for this script using getopts. After a few hours looking at this basic getopts guide and scouring SO, this is what my code looks so far:
#!/usr/bin/env bash
generate_url=false # set to true if -l is passed with no arg
markdown=false # true -> surround the URL with markdown
# Parse options
while getopts ':tab:md:l:' opt
do
case "$opt" in
a) service=appveyor ;;
b) branch=$OPTARG ;;
d) description=$OPTARG ;;
l) url=$OPTARG ;;
m) markdown=true ;;
t) service=travis ;;
# Colon: runs if no args are passed to
# an option that normally requires parameters.
:) test "$OPTARG" = l && generate_url=true ;;
# ?: Runs if an invalid option is passed.
'?') die ;;
esac
done
As you can see, most of the functionality is there, but I'm wondering how to accept repo as an argument to the script. Since getopts stops parsing after it encounters the first non-option argument, I'm wondering, how would I implement this (preferably with minimal complexity)? The guide I linked earlier doesn't seem to mention dealing with arguments that aren't associated with an option, so I'm a bit lost.
Thanks for helping!
Use $OPTIND value. After getopts cycle:
shift $((OPTIND-1))
echo $#
echo $1 $2 ...

How get next to next argument in linux shell script?

I have wrote simple shell script test.sh as follows:
while getopts ":A:" OPTION
do
case $OPTION in
A)
echo $OPTARG
?)
echo "no option"
esac
done
And executed the scripts as follows
$ ./test.sh -A 1 2
Now if got argument 1 by $OPTARG but how can i access the second argument ( 2 in this case)?
Regards
Jayesh
There are several options.
(1) You can use shift and take $1
while -n "$1"
do
# do something with $1
shift
done
(2) You can iterate through the args:
for i
do
# do something with $i
done
There are other alternatives also.

switches for shell script

what is the best way to implement switches [ex: -m] for shell scripts?
I can do it via the switch case statement. But i am curious to know is there any other standard way to get all the arguments into a variable via a switch.
Ex:
-m A1 A2 -c c1 c2
So that,
M[] can take -m
and C[] can all take -c
The best known way is to use getopts, see http://wiki.bash-hackers.org/howto/getopts_tutorial
An example :
#!/bin/bash
while getopts ":a" opt; do
case $opt in
a)
echo "-a was triggered!" >&2
;;
\?)
echo "Invalid option: -$OPTARG" >&2
;;
esac
done

Prevent ssh from breaking up shell script parameters

I have a script, which is essentially a wrapper around an executable by the same name on a different machine. For the sake of example, i'll wrap printf here. My current script looks like this:
#!/bin/bash
ssh user#hostname.tld. printf "$#"
Unfortunately, this breaks when one of the arguments contains a space, e.g. i'd expect the following commands to give identical outputs.:
~$ ./wrap_printf "%s_%s" "hello world" "1"
hello_world1_
~$ printf "%s_%s" "hello world" "1"
hello world_1
The problem gets even worse when (escaped) newlines are involved. How would I properly escape my arguments here?
Based on the answer from Peter Lyons, but also allow quotes inside arguments:
#!/bin/bash
QUOTE_ARGS=''
for ARG in "$#"
do
ARG=$(printf "%q" "$ARG")
QUOTE_ARGS="${QUOTE_ARGS} $ARG"
done
ssh user#hostname.tld. "printf ${QUOTE_ARGS}"
This works for everything i've tested so far, except newlines:
$ /tmp/wrap_printf "[-%s-]" "hello'\$t\""
[-hello'$t"-]
#!/bin/sh
QUOTE_ARGS=''
for ARG in "$#"
do
QUOTE_ARGS="${QUOTE_ARGS} '${ARG}'"
done
ssh user#hostname.tld. "${QUOTE_ARGS}"
This works for spaces. It doesn't work if the argument has an embedded single quote.
Getting quoting right is pretty hard and doing it in bash (in a general and robust way) almost impossible.
Use Perl:
#!/usr/bin/perl
use Net::OpenSSH;
my $ssh = Net::OpenSSH->new('user#hostname');
$ssh->system('printf', #ARGV);
Based on the answers from Koert and Peter Lyons, here a wrapper for ssh; i call it "sshsystem". (also available at https://gist.github.com/4672115)
#!/bin/bash
# quote command in ssh call to prevent remote side from expanding any arguments
# uses bash printf %q for quoting - no idea how compatible this is with other shells.
# http://stackoverflow.com/questions/6592376/prevent-ssh-from-breaking-up-shell-script-parameters
sshargs=()
while (( $# > 0 )); do
case "$1" in
-[1246AaCfgKkMNnqsTtVvXxYy])
# simple argument
sshargs+=("$1")
shift
;;
-[bcDeFIiLlmOopRSWw])
# argument with parameter
sshargs+=("$1")
shift
if (( $# == 0 )); then
echo "missing second part of long argument" >&2
exit 99
fi
sshargs+=("$1")
shift
;;
-[bcDeFIiLlmOopRSWw]*)
# argument with parameter appended without space
sshargs+=("$1")
shift
;;
--)
# end of arguments
sshargs+=("$1")
shift
break
;;
-*)
echo "unrecognized argument: '$1'" >&2
exit 99
;;
*)
# end of arguments
break
;;
esac
done
# user#host
sshargs+=("$1")
shift
# command - quote
if (( $# > 0 )); then
# no need to make COMMAND an array - ssh will merge it anyway
COMMAND=
while (( $# > 0 )); do
arg=$(printf "%q" "$1")
COMMAND="${COMMAND} ${arg}"
shift
done
sshargs+=("${COMMAND}")
fi
exec ssh "${sshargs[#]}"
The easiest and quickest is to just use Bash's Quoting Parameter Transformation: ${parameter#Q}. This can automatically applied during array expansion with ${array[#]#Q}, but when using the builtin argument array, the name and the brackets are dropped, so it becomes ${##Q}. Therefore the original script only needs 4 characters added to it to work.
#!/bin/bash
ssh user#hostname.tld. printf "${##Q}"
Now any escaping will work, even terminal colors like this:
./wrap_printf "%s\e[39m\e[49m\n" $'\e[30m\e[42mBlack on Green' "Just Normal Text"

Resources