bash script does not assign flags to variable - linux

I have a bash file where I am passing arguments like
bash foo.sh update -f /var/ -v true
so according to this answer my script should look like
if [[ "$1" == "update" ]]; then
updater
fi
function updater(){
verbose='false'
fflag=''
error=''
while getopts 'f:v' flag; do
case "${flag}" in
f) fflag="${OPTARG}";;
v) verbose='false';;
*) error="bflag";;
esac
done
echo $fflag
}
I am using the first script as an entry point, because I have other function that do other things, but for some reason the script above does not even show the value of the $fflag I tried moving out the getopts loop out of the function to no avail

There are 3 issues:
Define a function first at the start and then call it in your script
You need to pass command line to your function using "$#"
Before passing command line arguments call shift to remove first argument
You can use this script:
updater() {
verbose='false'
fflag=''
error=''
while getopts 'f:v' flag; do
case "$flag" in
f) fflag="${OPTARG}";;
v) verbose='false';;
*) error="bflag";;
esac
done
declare -p fflag
}
if [[ $1 == "update" ]]; then
shift
updater "$#"
fi

Related

getopts doesn't work when there are arguments before options

When I run the command like this :
$: ./script -r f1 f2 :
it detects the "-r" flag and sets the recursive flag to 1.
$: ./script directory/ -r :
getopts doesn't detect the -r flag at all. So inside the case statement it never detects -r flag and so the while loop doens't even run at all. how to fix this ?
RECURSIVE_FLAG=0
while getopts ":rR" opt ; do
echo " opt = $opt"
set -x
case "$opt" in
r) RECURSIVE_FLAG=1 ;;
R) RECURSIVE_FLAG=1 ;;
:)echo "not working" ;;
*)echo "Testing *" ;;
esac
done
It has nothing to do with slash. getopts stops processing options when it gets to the first argument that doesn't begin with -. This is the documented behavior:
When the end of options is encountered, getopts exits with a return value greater than zero. OPTIND is set to the index of the first non-option argument, and name is set to ?.
Your claim that it works when you use
./script f1 f2 -r
is simply wrong. I added echo $RECURSIVE_FLAG to the end of your script, and when I ran it that way it echoed 0.
If you want to allow a more liberal syntax, with options after filenames (like GNU rm) you'll need to do some argument parsing of your own. Put your getopts loop inside another loop. When the getopts loop finishes, you can do:
# Find next option argument
while [[ $OPTIND <= $# && ${!OPTIND} != -* ]]; do
((OPTIND++))
done
# Stop when we've run out of arguments
if [[ $OPTIND > $# ]]; then
break
fi

How to check number of arguments using a bash script?

How do I format in a script the number of arguments passed through a bash script? This what I have currently that works:
#!/bin/bash
echo "$# parameters"
echo "$#"
But I wanted to format is using a function but everytime I run it, it comes back as 0 parameters:
#!/bin/bash
example()
{
echo "$# parameters"; echo "$#";
}
example
Am I thinking about this incorrectly?
You are not passing the arguments to your function.
#! /bin/bash
EXE=`basename $0`
fnA()
{
echo "fnA() - $# args -> $#"
}
echo "$EXE - $# Arguments -> $#"
fnA "$#"
fnA five six
Output:
$ ./bash_args.sh one two three
bash_args.sh - 3 Arguments -> one two three
fnA() - 3 args -> one two three
fnA() - 2 args -> five six
It's the POSIX standard not to use the function keyword. It's supported in bash for ksh compatibility.
EDIT: Quoted "$#" as per Gordon's comment - This prevents reinterpretation of all special characters within the quoted string
The second one should work. Following are few similar examples I use every day that has the same functionality.
function dc() {
docker-compose $#
}
function tf(){
if [[ $1 == "up" ]]; then
terraform get -update
elif [[ $1 == "i" ]]; then
terraform init
else
terraform $#
fi
}
function notes(){
if [ ! -z $1 ]; then
if [[ $1 == "header" ]]; then
printf "\n$(date)\n" >> ~/worknotes
elif [[ $1 == "edit" ]]; then
vim ~/worknotes
elif [[ $1 == "add" ]]; then
echo " • ${#:2}" >> ~/worknotes
fi
else
less ~/worknotes
fi
}
PS: OSX I need to declare function you may not need that on other OSes like Ubuntu

Command line argument not working when passing to a function while using getopts

I've been trying to get some practice in with bash shell scripting but I've been having trouble using the $1 variable to reference the first argument for my script. It's a simple script that takes a file as an argument and prints the name of the file. Here's my script:
#!/bin/bash
function practice() {
echo "${1}"
}
while getopts "h:" opt; do
case "$opt" in
h) practice
;;
esac
done
exit 0
I tried the following command:
./practice.sh -h somefile.txt
For some reason it returns an empty line. Any thoughts?
$1 in functions refers to first positional parameter passed to that function, not passed to your script.
Therefore, you have to pass the arguments you want to the function again. You also tell getopts you want to process -h but then you are checking for -a in your case instead:
#!/bin/bash
practice() {
echo "${1}"
}
while getopts "h:" opt; do
case "$opt" in
h) practice "${OPTARG}"
;;
esac
done
#!/bin/bash
function practice() {
echo "${1}"
}
while getopts "h:" opt; do
case "$opt" in
a) practice $*
;;
esac
done
exit 0
pass the command line arguments to the function as above.

Bash Programming how to call "$i+1"

I am writing a script that will allow me to change a char in a string from "#" to something else, if I call an argument in terminal.
eg if I write
./myprogram testText.txt -r a
the -r argument will remove all "#" from testTxt.txt and replace them with "a"
My problem is I do not know how to write "If -r is $x, $x+1 is the char I want for replacement"
This is purely a syntax problem, I'm a bash noob :P. Here is the part of code I'm trying to work with.
for i in $*
do
if [[ $i = "-r" ]]
then
$customHashChoice=$((i+1))
# ^^^^^ Problematic Line ^^^^
fi
done
Try this:
customHashChoice=($(getopt "r:" "$#" 2>/dev/null))
if [ "${customHashChoice[0]}" == "-r" ]; then
customHashChoice="${customHashChoice[1]}"
else
echo "-r option is missing. Aborting..."
exit 1
fi
Syntax: getopt optstring parameters
From manual: getopt is used to break up (parse) options in command lines for easy parsing by shell procedures, and to check for legal options. It uses the GNU getopt(3) routines to do this.
Here, optstring is r:. It means, that the script accepts an option -r & the option takes an argument (implied by :).
The output of getopt "r:" "$#" is as below:
-r <argument to -r option> -- <unmatched parameters>
e.g. for command-line arguments,
./myprogram testText.txt -r a
getopt "r:" "$#" returns
-r a -- testText.txt
This output is stored in array & the second element of array is used, if the first element is equal to -r.
i=1
while [ "$i" -le $# ]
do
if [[ ${!i} = "-r" ]]
then
i=$(($i + 1))
customHashChoice=${!i}
i=$(($i + 1))
continue
fi
# do something useful
i=$(($i + 1))
done
The command line arguments are numbered 1 through $#. The above loops through each of them. If first checks if the current argument is -r and, if so, sets customHashChoice.
In the above, i contains the argument number. So, $i gives the value of i. To access the i'th command line argument, one uses ${!i}.
A more standard approach
The standard way to process command line arguments in shell scripts is getopts. It can handle many options. Here is sample code that that takes an option -r and requires it to have an argument, which is assigned to the shell variable char:
while getopts r: arg ; do case $arg in
r) char="$OPTARG" ;;
:) echo "${0##*/}: Must supply an argument to $OPTARG." ; exit 1 ;;
\?) echo "Invalid option" ; exit 1 ;;
esac
done
shift $(($OPTIND - 1))
echo "I will replace # with $char in file $1"
For getopts to work, the options have to come first. So, your command line would becomes:
./myprogram -r a testText.txt
If this is not acceptable, you can roll your own custom option processor. In the long run, there is some advantage, however, to standardizing on the usual approach.
You could do something like the following:
#!/bin/bash
val=
xval=
fname=$1
while [ "$*" != "" ]; do
case $1 in
"-r") val="${2}"; shift ;;
"-x") xval="${2}"; shift ;;
esac
shift
done
echo ${fname} ${val} ${xval}
Then when you pass the command like so
./myprogram testText.txt -r a
fname will be testText.txt, and the arguments will be parsed (where the -r will pick up a); for any other values you might want to parse, you'll need variable names to assign and test against. The output would be:
testText.txt a
Hope that helps

concatenate inputs in bash script [duplicate]

This question already has answers here:
Concatenate all arguments and wrap them with double quotes
(6 answers)
Closed 5 years ago.
I would like to concatenate all the arguments passed to my bash script except the flag.
So for example, If the script takes inputs as follows:
./myBashScript.sh -flag1 exampleString1 exampleString2
I want the result to be "exampleString1_exampleString2"
I can do this for a predefined number of inputs (i.e. 2), but how can i do it for an arbitrary number of inputs?
function concatenate_args
{
string=""
for a in "$#" # Loop over arguments
do
if [[ "${a:0:1}" != "-" ]] # Ignore flags (first character is -)
then
if [[ "$string" != "" ]]
then
string+="_" # Delimeter
fi
string+="$a"
fi
done
echo "$string"
}
# Usage:
args="$(concatenate_args "$#")"
This is an ugly but simple solution:
echo $* | sed -e "s/ /_/g;s/[^_]*_//"
You can also use formatted strings to concatenate args.
# assuming flag is first arg and optional
flag=$1
[[ $1 = ${1#-} ]] && unset $flag || shift
concat=$(printf '%s_' ${#})
echo ${concat%_} # to remove the trailing _
nJoy!
Here's a piece of code that I'm actually proud of (it is very shell-style I think)
#!/bin/sh
firsttime=yes
for i in "$#"
do
test "$firsttime" && set -- && unset firsttime
test "${i%%-*}" && set -- "$#" "$i"
done
IFS=_ ; echo "$*"
I've interpreted your question so as to remove all arguments beginning with -
If you only want to remove the beginning sequence of arguments beginnnig with -:
#!/bin/sh
while ! test "${1%%-*}"
do
shift
done
IFS=_ ; echo "$*"
If you simply want to remove the first argument:
#!/bin/sh
shift
IFS=_ ; printf %s\\n "$*"
flag="$1"
shift
oldIFS="$IFS"
IFS="_"
the_rest="$*"
IFS="$oldIFS"
In this context, "$*" is exactly what you're looking for, it seems. It is seldom the correct choice, but here's a case where it really is the correct choice.
Alternatively, simply loop and concatenate:
flag="$1"
shift
the_rest=""
pad=""
for arg in "$#"
do
the_rest="${the_rest}${pad}${arg}"
pad="_"
done
The $pad variable ensures that you don't end up with a stray underscore at the start of $the_rest.
#!/bin/bash
paramCat () {
for s in "$#"
do
case $s in
-*)
;;
*)
echo -n _${s}
;;
esac
done
}
catted="$(paramCat "$#")"
echo ${catted/_/}

Resources