Namespace problem with pipe into a braced group
Configuration Information [Automatically generated, do not change]: Machine: x86_64 OS: linux-gnu Compiler: gcc -I/usr/src/packages/BUILD/bash-3.1 -L/usr/src/packages/BUILD/bash-3.1/../readline-5.1 Compilation CFLAGS: -DPROGRAM='bash' -DCONF_HOSTTYPE='x86_64' -DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='x86_64-suse-linux-gnu' -DCONF_VENDOR='suse' -DLOCALEDIR='/usr/share/locale' -DPACKAGE='bash' -DSHELL -DHAVE_CONFIG_H -I. -I. -I./include -I./lib -O2 -fmessage-length=0 -Wall -D_FORTIFY_SOURCE=2 -g -D_GNU_SOURCE -DRECYCLES_PIDS -Wall -pipe -g -fPIE -fbranch-probabilities uname output: Linux t60 2.6.18.8-0.13-default #1 SMP Thu Nov 6 13:35:13 UTC 2008 x86_64 x86_64 x86_64 GNU/Linux Machine Type: x86_64-suse-linux-gnu Bash Version: 3.1 Patch Level: 17 Release Status: release Description: I would have expected the output to be 2 in both cases. My mistake or yours? :-) Repeat-By: a=1; echo foo;{ a=2; }; echo $a # output: 2 a=1; echo foo|{ a=2; }; echo $a # output: 1 Bug or feature?
Re: SHELLOPTS environment variable causes shell options to cascade
> | It is not possible to have a naive SHELLOPTS=myopt configured outside > | the shell without all future options being exported implicitly. > I have never used SHELLOPTS, and have never tested this, but: > Does "unset SHELLOPTS" not work? It gives: bash: unset: SHELLOPTS: cannot unset: readonly variable > ps: as it is safe, and portable, to unset variables that are not > set, in any shell, there's no reason not to include the unset in > all sh scripts, whatever shell variant they are to be interpreted by. It may be safe, however, it is a bit tedious to such an extent where it seems like it's a misuse of SHELLOPTS to try to pre-initialise as an environment variable like this to enable an option.
Re: SHELLOPTS environment variable causes shell options to cascade
> This precdence question is really the only one. Is this issue serious > enough to change previous behavior in an incompatible way? To me it seems most intuitive for flags to be applied after the environment variable, but I do not mind too much about this. > No. That is not consistent with the historical behavior of SHELLOPTS, > and not consistent with the behavior of environment variables. Agreed, thanks for the detailed explanations. I see now that the patch does not make sense and would only make things more confusing. > I'm not sure why the setup-ocaml authors chose the method they did, but > it seems like that's where this issue starts. Yes, given that there is this side effect of storing what is normally a shell variable in the environment instead, this does not seem like the correct way to achieve the goal of a globally set option. > Maybe they could change things to run `export -n SHELLOPTS' where appropriate. There is not really a good place to put these `export -n SHELLOPTS` lines in this case. Users of setup-ocaml would have to add these lines to their bash scripts which does not seem like a good solution. I think, as you've said, the issue starts with misusing SHELLOPTS.
Re: SHELLOPTS environment variable causes shell options to cascade
> This is already documented in a couple of different places: Thanks for providing the relevant sections. This makes sense having read things more thoroughly, however, most people do not read documentation from top to bottom. Someone looking for a way to enable a shell option globally will likely find the SHELLOPTS documentation which says: > If this variable is in the environment when Bash starts up, each shell option > in the list will be enabled before reading any startup files. This line gives the impression that using SHELLOPTS the way setup-ocaml is using it is a reasonable idea, and does not give any indication that doing so is going to change shell option behaviour. From my perspective, it would be helpful to have had a line here that might discourage such misuse of SHELLOPTS, or at least point a user to read the other relevant sections before trying to add it to the environment. This way, the behaviour wouldn't come as a surprise. Here is another project that fell into the same trap: https://github.com/infertux/bashcov/issues/50. -- Tobi
SHELLOPTS environment variable causes shell options to cascade
Hello, I'd like to ask about an odd behaviour relating to the SHELLOPTS environment variable. This is present in version 5.2.37(1)-release and in the development version. In this case, the `nounset` option is only applied to the current shell and not to nested bash invocations: > set -o nounset; echo $SHELLOPTS; bash -c "echo \$SHELLOPTS" On the other hand, if there is a pre-existing SHELLOPTS environment variable (set outside the current shell), then the `nounset` option applies to all invocations of bash within the current shell. Even with the `+u` flag, it is not possible to avoid the `nounset` option being applied to the inner bash invocation. The specific case where I found this behaviour to be problematic is in setup-ocaml, which is a github action: https://github.com/ocaml/setup-ocaml/pull/915. It applies "SHELLOPTS=igncr" globally so that it affects all cygwin bash invocations, however, a side effect is that other shell options set via bash option flags cascade down into nested bash invocations. Specifically, the `nounset` option applies to places where it didn't previously apply (which breaks a configure script of libuv). I know `igncr` only applies to cygwin bash, but it can be substituted for any option in regular bash and the same behaviour occurs. To be clear, the intention here was to apply a specific shell option globally (namely igncr), and the unintentional side effect was that the behaviour of how other shell options are applied has changed. I'm not sure if this behaviour of SHELLOPTS is intentional, but it is definitely confusing particularly from the lens of the given use case. This behaviour is not explicitly documented either: https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#index-SHELLOPTS. It seems the solution would be to either: 1) Document this cascading behaviour of SHELLOPTS, and decide on the correct behaviour of +u versus SHELLOPTS=nounset. or 2) Re-export only the initial value of SHELLOPTS to the new environment, prior to modifications due to `set` or bash option flags. I've added a patch below that achieves this, if it seems reasonable then I will write a test and format it properly as a patch request. Regards, Tobi --- variables.c | 21 +++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/variables.c b/variables.c index 1a0c2c45..ba65b97f 100644 --- a/variables.c +++ b/variables.c @@ -177,6 +177,11 @@ char **export_env = (char **)NULL; static int export_env_index; static int export_env_size; +/* For the SHELLOPTS environment variable, we must save and re-export + the original value as at initialization, ignoring changes resulting from + other methods of modifying shell options. */ +static char *shell_opts_value = (char *)NULL; + #if defined (READLINE) static int winsize_assignment; /* currently assigning to LINES or COLUMNS */ #endif @@ -515,7 +520,12 @@ initialize_shell_variables (env, privmode) temp_var = bind_variable (name, string, 0); if (temp_var) { - VSETATTR (temp_var, (att_exported | att_imported)); + VSETATTR (temp_var, att_imported); + if (STREQ (name, "SHELLOPTS")) + shell_opts_value = string; + else + VSETATTR (temp_var, att_exported); + if (ro) VSETATTR (temp_var, att_readonly); } @@ -5129,7 +5139,14 @@ maybe_make_export_env () export_env = strvec_resize (export_env, export_env_size); environ = export_env; } - export_env[export_env_index = 0] = (char *)NULL; + + if (shell_opts_value) + { + export_env[export_env_index = 0] = mk_env_string ("SHELLOPTS", shell_opts_value, 0); + export_env[export_env_index = 1] = (char *)NULL; +} + else +export_env[export_env_index = 0] = (char *)NULL; /* Make a dummy variable context from the temporary_env, stick it on the front of shell_variables, call make_var_export_array on the ---
Re: SHELLOPTS environment variable causes shell options to cascade
>> On the other hand, if there is a pre-existing SHELLOPTS environment variable >> (set outside the current shell), then the `nounset` option applies to all >> invocations of bash within the current shell. > This is documented in the manual: >> A colon-separated list of enabled shell options. >> If this variable is in the environment when Bash starts up, each shell >> option in the list will be enabled before reading any startup files. This does not cover the specific behaviour I am concerned with. I would expect something like: "Additionally, if it is in the environment at start up, the variable is automatically exported and shell options enabled subsequently through other means will be included in the exported value." I do not find it surprising that the values in SHELLOPTS are applied, I find it surprising that the pre-existence of a SHELLOPTS environment variable results in any future shell options being "sticky". It is not possible to have a naive SHELLOPTS=myopt configured outside the shell without all future options being exported implicitly. Perhaps the problem is that the given use-case abuses SHELLOPTS as if it also fulfilled the role of a DEFAULT_SHELLOPTS environment variable, i.e. one that sets some initial shell options but does not imply that SHELLOPTS itself should be exported. > Try BASH_ENV instead: > https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#index-BASH_005fENV If this is the correct way to achieve this, that is fair enough. If SHELLOPTS is not intended to be used in this way then I would expect the documentation to include enough information that would discourage a user from falling into this trap. -- Tobi