This problem is covered here: https://stackoverflow.com/questions/12985178/bash-quoted-array-expansion and here: Why does my shell script choke on whitespace or other special characters? and here: How can we run a command stored in a variable? . Closing it as a dupe.
How do I write a bash script frontend that accepts an option that's optional to an internal program, with an argument string that may have spaces in it? This is a single argument even though it has spaces. The key here, and the reason why none of the questions I've found work, is that any type of quoting- be it a string or an array- breaks when the variable is empty. I need to have an unquoted variable, that needs to provide a quoted string when it's of nonzero length.
I'm fond of writing things like this: bash frontend.sh -x argToX
that accepts the -x argument and passes it along to a child executable. I may have my own options, but I don't want to eliminate the ability to pass certain things to the child. But! If -x
is not set, there will be no argument on the child's command line.
The contents of frontend.sh
might look something like this:
#!/bin/bash
argx=""
# stub of my while loop:
while getopts "x:" opt; do
case $opt in
x)
argx="-x $OPTARG"
;;
esac
done
#
# complicated bash code here, including other executables that are necessary for my workflow.
#
/path/to/useful/executable $argx
Since it's not quoted, if it's the empty string Bash will not give an argument to the executable at all. In this way, "argx" is ready to be fully used as an argument to the executable.
But what if argx contains a space? Then it's broken. In that case, I do something like this (note: those command lines might be fairly long and elaborate):
[ -n "$argx" ] && /path/to/useful/executable -x "$argx" || \
/path/to/useful/executable
But is there a way for me to store stuff in argx, such that:
- if it's empty, there's no argument whatsoever sent to the executable? (an empty string argument would probably cause the executable to throw an error)
- if it's nonempty, create two arguments: the -x, and the string (which may have spaces in it, but regardless, the string is presented as a single argument after the -x)
I'm thinking no, because I need an unquoted evaluation of a variable or command substitution, but if I do that, then $IFS will swing into action.
I could possibly change IFS. This works for my $argx with spaces in it, but if I have multiple arguments that I want to pass along, then simpler ones like "-t target" will be broken (a modified IFS will not allow the -t to be split from the "target").
I think I'm out of luck, but I wonder if there's some magic I'm not thinking of...
(Update: The more I think about it, the more I think that what I'm trying to do is circumvent Bash's word splitting mechanism, which would be pretty impossible. But I was wrong about that, because...)
(Update 2: Thanks guest, who pointed out the shell's parameter expansion trick! I tested it, and it works.) And
(Update 3: I was screwing up my arrays. This simple code does it: argx=("-x" "$OPTARG")
. Then all I need to do is add "${argx[@]}"
as an argument to my child executable. If not set, nothing is expanded and no argument appears, which is what I need.)
-x
and your argument with spaces or leave the array empty. – Freddy Apr 23 '20 at 01:50-x
out ofargx
. Then use parameter expansion${argx:+-x "$argx"}
. Also the getopts loop is missingcase $opt in ... esac
– guest Apr 23 '20 at 02:38argx=(-x "$OPTARG")
and/path/to/useful/executable "${argx[@]}"
- if you didn't do that, then you're using arrays wrong and this is still a dupe. – muru Apr 23 '20 at 15:12