9

I am writing a bash script that takes in some optional parameters. I want to translate them and pass them to another script. However, I'm having a hard time passing the optional parameters gracefully.

Here's a outline of what I managed to get working in pseudocode:

a.sh:

if arg1 in arguments; then
    firstArg="first argument"
fi
if arg2 in arguments; then
    secondArg="second argument"
fi

./b.sh $firstArg $secondArg "default argument"

Note the spaces in the arguments.

b.sh:

for arg in "$@"
do
    echo $arg
done

I want to call b.sh, optionally with firstArg and secondArg and a default argument like so:

./b.sh $firstArg $secondArg "default argument"

The problem with this is that if $firstArg or $secondArg are strings with spaces, they will be represented as multiple arguments, and the output will be something like:

first
argument
second
argument
default argument

Okay, that's easy to fix, let's capture the entire string of the arguments by adding quotes around it like so:

./b.sh "$firstArg" "$secondArg" "defaultArg"

Problem is if, for example, firstArg is not set, it results in a blank line (as it will interpret "" as a parameter), so the output will be something like:

(blank line here)
second argument
defaultArg

I've also tried constructing a string and passing it to the shell script, but it doesn't seem to work that way either (it interprets the whole string as an argument, even if I add separate the arguments with quotes).

Note that calling b.sh from my command line with the arguments quoted works fine. Is there a way to mimic how this works from within a bash script?

2
  • 1
    Aside: Just because I'm copying your lead and using .sh extensions on executables (that is, files with the +x permission against which an execv()-family call will succeed) doesn't mean it's a good idea. In general, commands in UNIX shouldn't have any extensions, no matter whether they're provided via a shell script, a Python script, a compiled binary, etc. Commented May 6, 2015 at 20:15
  • Can you explicitly clarify whether this is for code using #!/bin/sh or #!/bin/bash? Commented May 6, 2015 at 20:21

2 Answers 2

9

If you literally want to copy all arguments given, but add one more:

# this works in any POSIX shell
./b.sh "$@" defaultArg

Alternately, to explicitly pass firstArg and secondArg, but only if they exist (note that set-to-an-empty-value counts as "existing" here):

# this works in any POSIX shell
./b.sh ${firstArg+"$firstArg"} ${secondArg+"$secondArg"} defaultArg

If you want to treat set-to-an-empty-value as not existing:

# this works in any POSIX shell
./b.sh ${firstArg:+"$firstArg"} ${secondArg:+"$secondArg"} defaultArg

An alternate approach is to build up an array of arguments:

# this requires bash or another shell with arrays and append syntax
# be sure any script using this starts with #!/bin/bash
b_args=( )
[[ $firstArg ]] && b_args+=( "$firstArg" )
[[ $secondArg ]] && b_args+=( "$secondArg" )
b_args+=( "default argument" )
./b.sh "${b_args[@]}"

If you want something with the same flexibility as the array method, but without the compatibility issues, define a function; within it, you can safely override "$@" without impacting the rest of the script:

runB() {
  set --
  [ -n "$firstArg" ]  && set -- "$@" "$firstArg"
  [ -n "$secondArg" ] && set -- "$@" "$secondArg"
  ./b.sh "$@" "default argument"
}
Sign up to request clarification or add additional context in comments.

7 Comments

Isn't is quoting "$@" wrong here? It will not being subject of word splitting after that anymore.. isn't it?
Good answer! I didn't got the question correctly when posting the comment.
Thank you for your answer! The second and third blocks work fine. Was really scratching my head over this one.
Does the fourth form not work for you? Unless you're using a very old bash (pre-3.2), that's actually somewhat surprising. Could you describe how it fails?
I just haven't tried it, I'll try it out and see if it works.
|
1

Use an array:

args=()

if [ ... ]; then
    args+=( "first argument" )
fi

if [ ... ]; then
    args+=( "second argument" )
fi

./b.sh "${args[@]}" "default argument"

4 Comments

@EtanReisner No it doesn't because in OP's example $firstArg might not be set at all (but used) but if it does get set it will get set to a string literal (same in the above snippet).
Ah, I assumed those were placeholders for the actual first and second arguments and not literal strings (in the OP and also here) and since you left out the actual tests on the arguments themselves I didn't fill those in. As written this doesn't have that problem but as written this also is only half of a solution (and I think doesn't quite solve it correctly).
I get where you're coming from on this one. However, the OP giving ./b.sh "$firstArg" "$secondArg" "defaultArg" as a possible solution (and describing its only fault as passing through empty arguments rather than not providing them at all) indicates that the use of literal strings "first argument" and "second argument" was just sloppiness earlier in the question.
@CharlesDuffy I see; I'll admit I probably didn't fully get the question then but then again, this is just a matter of how the ifs are constructed seeing you also suggested this as a solution. Unless I'm still missing something :-)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.