Create shell command that parses arguments in function [duplicate] - bash

This question already has answers here:
Using getopts inside a Bash function
(3 answers)
Closed 3 years ago.
I am unable to parse flags in a function in a bash script.
I have a shell script admin.sh that I'm sourcing (i.e. source ./admin.sh). In that script I have function called command_to_do. The code below shows what I'm trying to accomplish. I want to be able to call command_to_do from the command line and process all arguments
#!/bin/bash
function command_to_do() {
echo "Args $#"
SYSTEM=false
UNINSTALL=false
PUSH=false
VERSION="debug"
while getopts "sup:" opt; do
printf "\n$opt\n"
case $opt in
s) echo "Setting SYSTEM to true"
SYSTEM=true
;;
p) echo "Setting VERSION to $OPTARG"
PUSH=true
VERSION=$OPTARG
;;
u) echo "Setting UNINSTALL to true"
UNINSTALL=true
;;
esac
done
printf "\nSystem: $SYSTEM\nVersion: $VERSION\n"
if [[ $UNINSTALL = true ]]; then
if [[ $SYSTEM = true ]]; then
echo "system uninstall"
else
echo "non-system uninstall"
fi
fi
}
I'm getting very strange results. The first time I run the command_to_do -s -p release, it correctly processes the flags and prints the expected results
Args -s -p release
s
Setting SYSTEM to true
p
Setting VERSION to release
System: true
Version: release
Every time after that, it doesn't seem to capture the flags. For instance, running that same command again gives:
Args -s -p release
System: false
Version: debug
If I remove the function declaration (function command_to_do() {}) and just have the code in the admin.sh file, everything seems to work fine every time, but I have to call the script with the dot syntax (./admin -s -p release). The reasoning behind why I don't want to use the dot syntax is not important. I just want to know if it's possible to do what I'm attempting and how.
I've read about passing the arguments to the function with command_to_do $#, but, as I'm trying to call the function by it's name from the command prompt, this is not applicable and it appears by the echo $# statement that the arguments are being passed to the function.
I'm new to writing bash scripts so any help would be appreciated.

The trick here is resetting OPTIND, which tells getopts where it is in the current argument list. As it is, your first call leaves OPTIND at a value telling getopts not to process the arguments it already saw.
See a demonstration at https://ideone.com/UvUyn2

Related

Command not found with first positional argument used more than once

I have a script in bash which basically creates a user and install all the necessary applications.
It works the way that it iterates through a couple of commands, where I put a variable at the end of the command (positional argument).
I've set it up this way
function Install
{
COMMANDS=(
"Do_1st_thing $1"
"Do_2nd_thing $1"
"Do_3rd_thing $1"
)
for CMD in "${COMMANDS[#]}" ; do
$CMD
done
}
Then I run it
Install first_argument
The problem is that the first command is successful, however every next command says "Command not found".
Does the first positional argument ($1) changes after the execution of the first command?
Would I have to "eval" the "$CMD" in the "for loop" to get it working?
Feel free to ask any question, I will do my best to answer them.
Thank you for your help,
Kris
You are declaring an array with the first argument hard-coded in. If $1 is "foo" you are declaring
COMMANDS=(
"Do_1st_thing foo"
"Do_2nd_thing foo"
"Do_3rd_thing foo"
)
Storing these commands in an array seems like a weird thing to do anyway. Just
Install () {
Do_1st_thing "$#"
Do_2nd_thing "$#"
Do_3rd_thing "$#"
}
If your commands don't all accept the same arguments, you need to refactor the code, but that seems to be outside the scope of your concrete question here.
If they do, you might also consider refactoring into
commands=(Do_1st_thing Do_2nd_thing Do_3rd_thing)
for cmd in "${commands[#]}"; do
"$cmd" "$#"
done
(Notice also Correct Bash and shell script variable capitalization)
Maybe see also http://mywiki.wooledge.org/BashFAQ/050
As this is a bash function, you don't need the word function to designate it as a function. You would therefore write the code as below:
#!/bin/bash
Install()
{
COMMANDS=(
"ls $1"
"stat $1"
"file $1"
)
for CMD in "${COMMANDS[#]}" ; do
$CMD
done
}
Install testfile.txt

getopts not working when called with an argument [duplicate]

This question already has answers here:
Why does getopts only work the first time?
(2 answers)
Closed 3 years ago.
This is my first attempt using getopts, and so far it hasn't been working for me. The code in my script is:
while getopts "s:" opt; do
case $opt in
s) subj=$OPTARG;;
\?) echo "Incorrect usage";;
esac
done
echo ""
echo $subj
When I try to run to script like this:
myScript.sh -s 100
I want it to echo the subject id number I've specified. So far though it just gives me a blank statement.
getopts uses the current value of OPTIND to know which argument to look at next. If you are using source to run your script, though, OPTIND never gets reset between calls. You probably added subj after the first run, so that its value wasn't set the first time you sourced the script. Explicitly setting OPTIND=1 would fix it.
$ source myScript.sh -s 100
100
$ unset subj; source myScript.sh -s 100
$ OPTIND=1
$ source myScript.sh -s 100
100

Bug in parsing args with getopts in bash

I was trying to modify the bd script to use getopts. I am a newbie at bash scripting
my script is
while getopts ":hvis:d:" opt
do
...
done
...
echo $somedirpath
cd "$somedirpath"
this runs fine when doing
$ ./bd -v -i -s search
or
$ ./bd -is search -d dir
But when running it like this
$ . ./bd -s search
getopts doesn't read the arguments at all. And all the variables I set in the while loop according to the arguments are all not set, so the script no longer works. Please help!
Setting OPTIND=1 before invoking getopts works fine.
The problem is that getopts relies on OPTIND to loop through the arguments provided, and after sourcing the script, it will be set to some value greater than 1 by getopts according to how many arguments you pass. This value gets carried over even after the script ends(because its being sourced). So the next time its sourced, getopts will pick up from that OPTIND, rather than starting from 1!
This might cause strange behaviour with other scripts, and I don't know how safe this is. But it works!
For a better workaround, I think what #tripleee suggests looks safe and robust.
When you source a script, the arguments parsed by getopts are those of the current shell, not the parameters on the source command line.
The common workaround is to have your script merely print the path, and invoke it like cd "$(bd)" instead (perhaps indirectly through a function or alias).
Setting OPTIND=1 may not work reliably on zsh. Try to use something different than getopts:
while [ "$#" -gt 0 ]
do
case "$1" in
-h|--help)
help
return 0
;;
-o|--option)
option
return 0
;;
-*)
echo "Invalid option '$1'. Use -h|--help to see the valid options" >&2
return 1
;;
*)
echo "Invalid option '$1'. Use -h|--help to see the valid options" >&2
return 1
;;
esac
shift
done

How to read arguments from bash [duplicate]

This question already has answers here:
Creating bash scripts that take arguments
(3 answers)
Closed 8 years ago.
I am curious as to how to pass in arguments via terminal to the bash script and read them and process the script functions based on the arguments.
So if I did something like:
./scriptname.sh install
#or
./scriptname.sh assets install
How would I say, ok the first argument installs something, while the second sais to do something else based on the first argument.
$0 is the name of the command
$1 first parameter
$2 second parameter
$3 third parameter etc. etc
$# total number of parameters
for args in $*
blah blah
One great way to pass arguments to a script is to use the bash builtin functionality getopts
you can use it like this:
# a script that accepts -h -a <argument> -b
while getopts "ha:b" OPTION
do
case $OPTION in
h)
# if -h, print help function and exit
helpFunction
exit 0
;;
a)
# -a requires an argument (because of ":" in the definition) so:
myScriptVariable=$OPTARG
;;
b)
# do something special
doSomeThingSpecial
;;
?)
echo "ERROR: unknonw options!! ABORT!!"
helpFunction
exit -1
;;
esac
done
You can access a particular argument with $1, $2, ... See eg What does "$1/*" mean in "for file in $1/*"
You can also use "$#" to loop on your arguments. Ex : https://github.com/gturri/dotfiles/blob/master/bootstrap.sh#L64

Using getopts within user-defined-function in bourne shell

Is it possible to pass command line arguments into a function from within a bourne script, in order to allow getopts to process them.
The rest of my script is nicely packed into functions, but it's starting to look like I'll have to move the argument processing into the main logic.
The following is how it's written now, but it doesn't work:
processArgs()
{
while getopts j:f: arg
do
echo "${arg} -- ${OPTARG}"
case "${arg}" in
j) if [ -z "${filename}" ]; then
job_number=$OPTARG
else
echo "Filename ${filename} already set."
echo "Job number ${OPTARG} will be ignored.
fi;;
f) if [ -z "${job_number}" ]; then
filename=$OPTARG
else
echo "Job number ${job_number} already set."
echo "Filename ${OPTARG} will be ignored."
fi;;
esac
done
}
doStuff1
processArgs
doStuff2
Is it possible to maybe define the function in a way that it can read the scripts args? Can this be done some other way? I like the functionality of getopts, but it looks like in this case I'm going to have to sacrifice the beauty of the code to get it.
You can provide args to getopts after the variable. The default is $#, but that's also what shell functions use to represent their arguments. Solution is to pass "$#" — representing all the script's command-line arguments as individual strings — to processArgs:
processArgs "$#"
Adding that to your script (and fixing the quoting in line 11), and trying out some gibberish test args:
$ ./try -j asdf -f fooo -fasdfasdf -j424pyagnasd
j -- asdf
f -- fooo
Job number asdf already set.
Filename fooo will be ignored.
f -- asdfasdf
Job number asdf already set.
Filename asdfasdf will be ignored.
j -- 424pyagnasd

Resources