Bash Script Argument Containing Spaces Not Working [duplicate] - bash

This question already has an answer here:
Passing argument containing space in shell script
(1 answer)
Closed 2 years ago.
I'm a bash noob and can't figure out how to use a string containing spaces in a bash argument. Here's the function giving me problems...
handle_arguments() {
ARGUMENTS=($#);
for i in ${ARGUMENTS[#]}
do
INDEX=`expr index "$i" =`;
case $i in
--projectslug*)
PROJECT_SLUG=${i:INDEX};
;;
--sitename*)
SITE_NAME=${i:INDEX};
;;
--build*)
BUILD_TYPE=${i:INDEX};
;;
--dbname*)
DB_NAME=${i:INDEX};
;;
--mysqlun*)
MYSQL_UN=${i:INDEX};
;;
--mysqlpw*)
MYSQL_PW=${i:INDEX};
;;
--dbfilename*)
DB_FILE_NAME=${i:INDEX};
;;
--search*)
DB_SR_SEARCH=${i:INDEX};
;;
--replace*)
DB_SR_REPLACE=${i:INDEX};
;;
*)
echo -e "${RED}$i is not recognized as an argument.${NC}\n";
;;
esac
done;
if [[ -z $DB_NAME ]];
then
DB_NAME=$PROJECT_SLUG;
fi
}
The command I run is...
$ source wpautobuild.sh; wp_auto_build --projectslug=abctest1 --sitename="ABC TEST 1"
I've been troubleshooting this for hours and everything I'm finding is telling me to wrap $# in ARGUMENTS=($#); with double quotes so it's ARGUMENTS=("$#"); but that does not work.
I've also tried adding double quotes to SITE_NAME=${i:INDEX}; to be SITE_NAME="${i:INDEX}"; but also with no luck.
One article suggested escaping spaces in the command like this...
$ source wpautobuild.sh; wp_auto_build --projectslug=abctest1 --sitename="ABC\ TEST\ 1"
...and that did not work either.
Note: The handle_arguments() function is run in the wp_auto_build() function which is being called in the command.
I'm totally stuck and appreciate any help I can get. Thanks in advance.

You need to quote $# for it to preserve spaces. Unquoted $# serves no purpose, as it is identical to unquoted $*.
ARGUMENTS=("$#")
You subsequently need to quote ${ARGUMENTS[#]} for the same reason
for i in "${ARGUMENTS[#]}"
though there is no need for ARGUMENTS at all; you can iterate over "$#" directly.
for i in "$#"

Related

Ways to provide list of parameters to options in shell scripts? [duplicate]

This question already has answers here:
How do I parse command line arguments in Bash?
(40 answers)
Closed 2 years ago.
Basically I am making a script where I would like to give a list of parameters to some option, say -l, eg:
usr#domain:$./somescript -l hi bye cry nigh
The list specified to -l would be then stored into a specific array and iterated over.
So far, I have been using these lines from Arch linux's mkinitcpio script to do so. This allows one to specify a comma-separated list to an option:
_optaddhooks()
while :; do
case $1 in
-A|--add|--addhooks)
shift
IFS=, read -r -a add <<< "$1"
_optaddhooks+=("${add[#]}")
unset add
;;
What are some other methods to do this, particularly when other options/parameters will be used? Can the list provided to an option be space separated? Or is specifying a list by "," (or some other character") easier to manage?
UPDATE: OP is looking for a non-getopts solution; adding a -l option to the current code:
NOTE: OP has shown the command line flag as -l so not sure what the -A|-add|--addhooks) relates to ... ???
unset _optaddhooks
while :; do
case "${1}" in
-A|--add|--addhooks)
shift
....
;;
-a) shift
vara="${1}"
;;
-l) shift
_optaddhooks=( ${1} )
;;
-z) shift
varz="${1}"
;;
....
esac
done
OP would still use the same command line as in the earlier answer (below):
$ ./somescript -a this_is_a -l 'hi bye cry nigh' -z 123456
If you're willing to wrap the -l args in quotes you could then pass the args as a single parameter and then have bash parse this parameter however you wish.
One idea to load the -l args into an array named _optaddhooks[]:
$ cat somescript
#!/usr/bin/bash
while getopts :a:l:z: opt
do
case ${opt} in
a) vara="${OPTARG}" ;;
l) _optaddhooks=( ${OPTARG} ) ;;
z) varz="${OPTARG}" ;;
*) echo "Sorry, don't understand the option '${opt}'" ;;
esac
done
typeset -p vara _optaddhooks varz
A sample run:
$ ./somescript -a this_is_a -l 'hi bye cry nigh' -z 123456
declare -- vara="this_is_a"
declare -a _optaddhooks=([0]="hi" [1]="bye" [2]="cry" [3]="nigh")
declare -- varz="123456"
While this was a simple example, it should be easy enough to modify the code (l section of the case statement) as needed.

How does a Bash for loop read script parameters? [duplicate]

This question already has answers here:
What is $opt variable for command line parameter in bash
(3 answers)
Closed 4 years ago.
Normally, script parameters are read from $1, $2, ...
Sometimes this is combined with shift and a while-loop and case-statement to process multiple parameters.
while [[ $# > 0 ]]; do
case "$1" in
-v|--verbose)
VERBOSE=1
;;
-d|--debug)
VERBOSE=1
DEBUG=1
;;
*) # unknown option
echo 1>&2 -e "${COLORED_ERROR} Unknown command line option '$key'.${ANSI_NOCOLOR}"
exit 1
;;
esac
shift # parsed argument or value
done
Today, I found a code snippet based on a simple for-loop:
#! /bin/bash
for opt; do
echo $opt
done
Execution:
$ ./test.sh foo bar spam
foo
bar
spam
Normally, one would see for i in ...; do.
Why/how can a simplified for-loop access script parameters?
Does it also work with parameters in functions?
From help for:
If in WORDS ...; is not present, then in "$#" is assumed.

How to quote bash flag arguments to pass through one getopts call and be interpreted by a second?

Script nerf calls script herd, which calls script er. nerf uses a flag on herd that explicitly takes arguments needing to be passed to er.
This was not a problem before nerf existed - when herd was just called from the command line, we could single-quote the arguments to the -p flag, and they would never be interpreted by herd's getopts, but instead they would be interpreted by er's getopts.
But now we have generated values in the flags that eventually need to go to er, so I need to expand the variable $file_contents in nerf, but not let them be interpreted by getopts until they get to er.
Any of these three scripts can be modified.
$ cat nerf
#!/bin/bash
file_contents="`cat one_liner_file`"
er_args="-jkl -m $file_contents"
./herd -p "$er_args" # <-- the problem
$ cat herd
#!/bin/bash
passthru_args=""
while getopts "p:a:b:cde" opt
do
case $opt in
p) passthru_args="$OPTARGS" ;;
...
esac
done
./er "$passthru_args"
$ cat er
#!/bin/bash
while getopts "jklm:" opt
do
case $opt in
...
esac
done
If I use single quotes on the marked line above, I get the literal string "$er_args" passed through. Using double quotes, the flags are directly interpreted by herd. Using single inside double quotes, the flags aren't interpreted by ANY getopts.
I'm thinking there's no elegant solution here, but please let me know if I'm wrong. The only solutions I can think of are crappy:
Expose all of er's flags explicitly through herd.
Remove the er call from herd and place it directly into nerf.
???
What many tools do is when passed -p "-jkl -m something", they split up the string using pseudo-shell syntax. This is a bad idea because it makes space and quote handling unpredictable.
Instead, the better way is to have a way to pass individual words to the command. This is what find -exec does -- all arguments after -exec and up until + or ; are passed literally as separate arguments.
Here's a simple example of a herd with the same semantics:
#!/bin/bash
passthru_args=()
while getopts "pa:b:cde" opt
do
case $opt in
p)
while [[ ${!OPTIND} != ';' ]]
do
passthru_args+=("${!OPTIND}")
let OPTIND++
done
let OPTIND++
;;
*) echo "herd: $opt is $OPTARG"
;;
esac
done
./er "${passthru_args[#]}"
You can now run ./herd -p -jkl -m "some stuff" \; -a foo
This will run ./er -jkl -m "some stuff" safely without any space issues (but you'll have a hard time nesting multiple calls that use ; as an argument terminator).

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 handle shell getopts with parameter containing blank spaces

I'm looking for a way to handle arguments containing blank spaces that has to be parsed
by shell getopts command.
while getopts ":a:i:o:e:v:u:" arg
do
echo "ARG is: $arg" >> /tmp/submit.log
case "$arg" in
a) arg1="$OPTARG" ;;
i) arg2="$OPTARG" ;;
o) arg3="$OPTARG" ;;
...
u) argn="$OPTARG" ;;
-) break ;;
\?) ;;
*) echo "unhandled option $arg" >> /tmp/submit.log ;;
?) echo $usage_string
exit 1 ;;
esac
done
Now if -u has argument like "STRING WITH WHITE SPACE"
than just the first part of the string is triggered and the while loop doesn't go to the end.
many thanks.
a trap for young players (ie me!)
beware a line like this:
main $#
what you really need is:
main "$#"
otherwise getopts will mince up your options into little pieces
http://www.unix.com/shell-programming-scripting/70630-getopts-list-argument.html
As Mat notes, your script fragment is already correct. If you're invoking your script from a shell, you need to quote arguments properly, e.g.
myscript -u "string with white space"
myscript -u 'string with white space'
myscript -u string\ with\ white\ space
myscript -u string' w'ith\ "whi"te" "''space
Requiring these quotes is not a defect in your script, it's the way the calling shell works. All programs, scripts or otherwise, receive arguments as a list of strings. The quotes in the calling shell are used to sort these arguments into separate “words” (list elements). All the calls above (made from a unix shell) pass a list of three strings to the script: $0 is the script name (myscript), $1 is -u and $2 is the string string with white space.

Resources