admin管理员组文章数量:1397086
While experimenting with inline environment variables, I noticed that although they are correctly applied to command execution, their expansion sometimes retains the previous value—but not always!
Context:
- Bash version:
GNU bash, version 5.2.21(1)-release (x86_64-pc-linux-gnu)
- System locale:
fr_FR.UTF-8
(floating-point numbers use a comma,
as the decimal separator)
Reproducing the issue:
Running the following bash script:
#!/usr/bin/env bash
locale
echo
LC_ALL=C bash -c 'printf "%s %f\t%f\n" "$LC_NUMERIC" 3.141592654 3,141592654'
echo
LC_NUMERIC=C bash -c 'printf "%s %f\t%f\n" "$LC_NUMERIC" 3.141592654 3,141592654'
Produces this output with some unexpected results:
LANG=fr_FR.UTF-8
LANGUAGE=fr_FR:fr_CA:en
LC_CTYPE="fr_FR.UTF-8"
LC_NUMERIC=fr_FR.UTF-8
LC_TIME=fr_FR.UTF-8
LC_COLLATE="fr_FR.UTF-8"
LC_MONETARY=fr_FR.UTF-8
LC_MESSAGES="fr_FR.UTF-8"
LC_PAPER=fr_FR.UTF-8
LC_NAME=fr_FR.UTF-8
LC_ADDRESS=fr_FR.UTF-8
LC_TELEPHONE=fr_FR.UTF-8
LC_MEASUREMENT=fr_FR.UTF-8
LC_IDENTIFICATION=fr_FR.UTF-8
LC_ALL=
bash: line 1: printf: 3,141592654: invalid number
fr_FR.UTF-8 3.141593 3.000000
bash: ligne 1 : printf: 3,141592654: nombre non valable
C 3.141593 3.000000
Observations:
- In the first test (
LC_ALL=C
), the floating-point numbers are correctly interpreted using a dot.
as the decimal separator, but the printed value of$LC_NUMERIC
remainsfr_FR.UTF-8
instead ofC
. - In the second test (
LC_NUMERIC=C
), the printed value correctly reflects the inline environment assignment.
Additionally, there’s another strange behaviour with inline environment variables:
Running:
LANG=C LC_NUMERIC=C printf '%s\t%f\t%f\n' "$LC_NUMERIC" 3.141592654 3,141592654
Outputs:
bash: printf: 3,141592654: invalid number
fr_FR.UTF-8 3.141593 3.000000
Even though LC_NUMERIC=C
is used to correctly parse 3.141592654
, the expansion of $LC_NUMERIC
still prints its original value (fr_FR.UTF-8
).
Conclusion:
This may not necessarily be a bug, but the behaviour seems counter-intuitive and inconsistent as well, making it difficult to predict how inline environment variables will affect both variable expansion and command execution.
Any insights on this would be greatly appreciated!
EDIT
Further investigations shared with the bug-bash public mailing-list show that it is a problem related to locale and the use of LC_ALL
.
While experimenting with inline environment variables, I noticed that although they are correctly applied to command execution, their expansion sometimes retains the previous value—but not always!
Context:
- Bash version:
GNU bash, version 5.2.21(1)-release (x86_64-pc-linux-gnu)
- System locale:
fr_FR.UTF-8
(floating-point numbers use a comma,
as the decimal separator)
Reproducing the issue:
Running the following bash script:
#!/usr/bin/env bash
locale
echo
LC_ALL=C bash -c 'printf "%s %f\t%f\n" "$LC_NUMERIC" 3.141592654 3,141592654'
echo
LC_NUMERIC=C bash -c 'printf "%s %f\t%f\n" "$LC_NUMERIC" 3.141592654 3,141592654'
Produces this output with some unexpected results:
LANG=fr_FR.UTF-8
LANGUAGE=fr_FR:fr_CA:en
LC_CTYPE="fr_FR.UTF-8"
LC_NUMERIC=fr_FR.UTF-8
LC_TIME=fr_FR.UTF-8
LC_COLLATE="fr_FR.UTF-8"
LC_MONETARY=fr_FR.UTF-8
LC_MESSAGES="fr_FR.UTF-8"
LC_PAPER=fr_FR.UTF-8
LC_NAME=fr_FR.UTF-8
LC_ADDRESS=fr_FR.UTF-8
LC_TELEPHONE=fr_FR.UTF-8
LC_MEASUREMENT=fr_FR.UTF-8
LC_IDENTIFICATION=fr_FR.UTF-8
LC_ALL=
bash: line 1: printf: 3,141592654: invalid number
fr_FR.UTF-8 3.141593 3.000000
bash: ligne 1 : printf: 3,141592654: nombre non valable
C 3.141593 3.000000
Observations:
- In the first test (
LC_ALL=C
), the floating-point numbers are correctly interpreted using a dot.
as the decimal separator, but the printed value of$LC_NUMERIC
remainsfr_FR.UTF-8
instead ofC
. - In the second test (
LC_NUMERIC=C
), the printed value correctly reflects the inline environment assignment.
Additionally, there’s another strange behaviour with inline environment variables:
Running:
LANG=C LC_NUMERIC=C printf '%s\t%f\t%f\n' "$LC_NUMERIC" 3.141592654 3,141592654
Outputs:
bash: printf: 3,141592654: invalid number
fr_FR.UTF-8 3.141593 3.000000
Even though LC_NUMERIC=C
is used to correctly parse 3.141592654
, the expansion of $LC_NUMERIC
still prints its original value (fr_FR.UTF-8
).
Conclusion:
This may not necessarily be a bug, but the behaviour seems counter-intuitive and inconsistent as well, making it difficult to predict how inline environment variables will affect both variable expansion and command execution.
Any insights on this would be greatly appreciated!
EDIT
Further investigations shared with the bug-bash public mailing-list show that it is a problem related to locale and the use of LC_ALL
.
1 Answer
Reset to default 1Given this ...
#!/usr/bin/env bash locale echo LC_ALL=C bash -c 'printf "%s %f\t%f\n" "$LC_NUMERIC" 3.141592654 3,141592654' echo LC_NUMERIC=C bash -c 'printf "%s %f\t%f\n" "$LC_NUMERIC" 3.141592654 3,141592654'
... I observe the appearances of $LC_NUMERIC
are expanded by the child shells, in which the outer variable assignment is in effect, not by the shell in which you are issuing the command with inline variable assignment. Given that context, we can explain the observations:
- In the first test (
LC_ALL=C
), the floating-point numbers are correctly interpreted using a dot.
as the decimal separator, but the printed value of$LC_NUMERIC
remainsfr_FR.UTF-8
instead ofC
.
Yes and yes.
The LC_NUMERIC
variable is significant to Bash, but it is nevertheless a regular environment variable, not a shell parameter. Bash does not manage its value. In particular, Bash does not adjust it based on the value of LC_ALL
, so it is natural that your child process inherits the parent's value for this variable, and that that's the value printed in your test.
But that value is mooted by the fact that LC_ALL
is set. When set, this variable overrides all the category-specific locale variables, so when you're operating under the effect of LC_ALL=C
, you get the period character (.
) as the decimal separator regardless of the value of LC_NUMERIC
.
- In the second test (
LC_NUMERIC=C
), the printed value correctly reflects the inline environment assignment.
Yes. In this example you pass that value for LC_NUMERIC
in the child process's environment, so, naturally, that is the value to which it expands in that process. And that is sufficient to get the period used as the decimal separator.
Note well that none of this has anything in particular to do with the variable assignments being inline.
Additionally, there’s another strange behaviour with inline environment variables [...]
Not really. This one is the common issue that @chepner was referencing in comments. There are numerous dupes, but as long as I'm answering the above, I'll address this, too. Here ...
LANG=C LC_NUMERIC=C printf '%s\t%f\t%f\n' "$LC_NUMERIC" 3.141592654 3,141592654
... the first field of the output reflects how $LC_NUMERIC
was expanded by the shell in which that command was issued. The inline variable assignment does not affect that shell, not even temporarily. It affects only the environment of the executed command (printf
in this case). But it does affect that command's environment, so it has its normal effect on printf
's choice of formatting characters.
And note here that whereas LC_ALL
that you tested before is an all-category override, the LANG
variable that you're using here is an all-category default. Thus, if you specified a different LANG
, you would still get numeric formatting according to the locale specified by LC_NUMERIC
.
本文标签: bashUnexpected behaviours with inline environment variables expansionStack Overflow
版权声明:本文标题:bash - Unexpected behaviours with inline environment variables expansion - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744149524a2592997.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
"$LC_NUMERIC"
is expanded beforeprintf
executes in an environment withLC_NUMERIC=C
. – chepner Commented Mar 26 at 11:21LC_ALL
is used in favor of the value ofLC_NUMERIC
, rather than changing the value ofLC_NUMERIC
. – chepner Commented Mar 26 at 11:24foo=bar echo "$foo"
doesn't emitbar
, then we have applicable duplicates; so clarification really is needed) – Charles Duffy Commented Mar 26 at 13:49foo=bar printenv foo
, where you don't have a$
. You will see that foo has the desired value. – user1934428 Commented Mar 27 at 11:36