Went through this post: Pass arguments to function exactly as-is
But have a slightly different setup:
I have 3 bash functions foo, bar, baz. They are setup as follows:
foo() {
bar 1ドル
}
bar() {
var1=1ドル
var2=2ドル
echo "$var1" test "$var2"
}
export ENV_VAR_1="1"
export ENV_VAR_2="2 3"
foo "${ENV_VAR_1} ${ENV_VAR_2}"
I'd expect the output to be:
1 test 2 3
But the output is:
1 test 2
I get why this happened. bar was executed as follows:
bar 1 2 3
My question is: how do I get it to execute
bar 1 "2 3"
Approaches I tried:
foo () {bar "1ドル"}
# Out: 1 2 3 test. Makes sense since "1 2 3" is interpreted as a single argument.
2 Answers 2
This provides a single string as an argument to foo
:
foo "${ENV_VAR_1} ${ENV_VAR_2}"
Because 1ドル
is not in quotes, the shell performs word splitting and, consequently, this provides three arguments to bar
:
bar 1ドル
Word splitting is done on any IFS characters in S1
. The original source of those characters is not considered.
Simpler example
Let's define x
as:
$ x="${ENV_VAR_1} ${ENV_VAR_2}"
Now, let's print "$x"
:
$ printf "%s\n" "$x"
1 2 3
As you can see, "$x"
is interpreted as one argument. By contrast, consider:
$ printf "%s\n" $x
1
2
3
In the above, word-splitting is performed on $x
creating three arguments.
Shell strings have no notion of history. String x
has no record of 2 3
being part of one string before x
was assigned. String x
just consists of 1
, space, 2
, space, and 3
and word splitting operates on the spaces.
Alternative: selecting your own IFS
This produces the output that you want:
$ foo() ( IFS=@; bar 1ドル; )
$ foo "${ENV_VAR_1}@${ENV_VAR_2}"
1 test 2 3
In foo
, we set IFS
to @
. Consequently, all subsequent word splitting is performed using @
as the word separator. So, when calling foo
, we put a @
at any location at which we want word splitting.
-
I understand now why it's happening. But I'm wondering if there's any way around it. Adding escaped quotes to the input sequence doesn't seem to help.user2103008– user21030082016年09月06日 03:01:54 +00:00Commented Sep 6, 2016 at 3:01
-
@user2103008 I added code showing how to get the string to split where you want it to. Adding escaped quotes will not help unless
eval
is used and an unnecessary use ofeval
can lead to a variety of surprising problems.John1024– John10242016年09月06日 03:09:02 +00:00Commented Sep 6, 2016 at 3:09 -
I agree. I ended up refactoring so that I don't have to use space separated arguments within the argument string. I'll still accept the answer you gave since it makes sense.user2103008– user21030082016年09月06日 14:40:12 +00:00Commented Sep 6, 2016 at 14:40
-
1Why would you stuff
${ENV_VAR_1}
and${ENV_VAR_2}
together? That's the root of the problem. Keep them separate!Gilles 'SO- stop being evil'– Gilles 'SO- stop being evil'2016年09月07日 00:53:51 +00:00Commented Sep 7, 2016 at 0:53
You're passing a single argument to the function foo
, which is the 5-character string 1 2 3
. The three digits are separated by spaces. foo
has no reason to treat those spaces differently.
In combining ${ENV_VAR_1}
and ${ENV_VAR_2}
inside a single string, you've lost information. Recovering lost information requires magic, and computers can't do magic. To retain the separation between 1 2
and 3
, you need to change the way foo
is called. Pass two separate arguments.
foo "${ENV_VAR_1}" "${ENV_VAR_2}"
Then all you need to do is fix the quoting in the call to bar
. Either pass on the two parameters:
foo () {
bar "1ドル" "2ドル"
}
or pass on the whole parameter list:
foo () {
bar "$@"
}
foo () {bar "1ドル"}
is a syntax error. If you want a one-line function definition, you needfoo() { bar "1ドル";}
Note the space and the semicolon.