6

I have a shell script without the first line that specifies which shell type to use to interpret commands.

This .sh file has been used on a SCO Unix 5 system until the moment of migration on a more modern system like RHEL 7.

It seems to me that sh is the default shell in SCO Unix while the bash is the default shell in Red Hat Linux, so I think that porting the script to linux and running it, this one will be interpreted by default with the bash.

Coming to the point, in this script there is a section like this:

MY_SETUP=1
echo $MY_SETUP
MY_SETUP=2 export MYSETUP
echo $MY_SETUP

As you can see the name of the variable after the export command is not the one in the assignment (it was a typing mistake).

I noticed that the value of MY_SETUP variable after this section is different if the script is interpreted by sh or bash.

  • sh MY_SETUP value = 2

  • bash MY_SETUP value = 1

It seems that bash completely ignores the assignment inline with the export command and keeps the previous value.

All this run without returning any error, so I was wondering why there is a different behavior. Can someone explain me?

EDIT:

From Stéphane Chazelas answer it seems that in bash this instruction

var=x export var

doesn't set the "x"value and doesn't export it, but in my environment it does both. I'm confused.

  • You're right about your edit, looks like I messed up my tests, I'll update my answer. – Stéphane Chazelas Jun 8 '18 at 11:42
2

It seems that you detected a POSIX deviation in bash that is also a deviation from the historic Bourne Shell. You may call it a bug or a just deviating behavior.

The script you refer to prints

1
2

with all shells except bash in default behavior.

If you call bash --posix, it works correctly.

From a pointer from user Kusalananda it seems that bash by default makes all builtin commands restore their temporary environment at exit and not just for the non-special builtins. Since export is a special builtin, POSIX requires a shell to behave the same as a Bourne Shell from the early 1980s end to keep the environment value.

Since bash does not implement this by default, you get the deviation.

  • bash in its POSIX mode does the right thing though. – Kusalananda Jun 7 '18 at 15:22
  • Point 30 in Chet's list of differences between bash's default mode of operation and bash --posix may be relevant, possibly. – Kusalananda Jun 7 '18 at 15:27
  • It would be a bug if it didn't work as documented. Here bash behaves as required by POSIX when in POSIX mode, like when called as sh, and works as documented (and some would argue in a less surprising way) otherwise. – Stéphane Chazelas Jun 7 '18 at 15:36
  • Note that zsh behaves like bash here, so it's not all shells except bash. – Stéphane Chazelas Jun 7 '18 at 15:38
  • I tested zsh, so it may be that zsh changed behavior between different releases. – schily Jun 7 '18 at 15:40
0

Yes, the behavior is different. The whole description is not so simple.

First: when is it equal?

This code line :

$ var=1; printf "%s" "$var"; var=2 export var; echo " $var"

Will print 1 2 in all (non-csh like) shells except zsh.

jsh             : 1 2      # ATT version sh (heirloom).
ash             : 1 2
yash            : 1 2
dash            : 1 2
zsh/sh          : 1 2
bash            : 1 2
posixbash       : 1 2
lksh            : 1 2
mksh            : 1 2
ksh93           : 1 2
attsh           : 1 2
zsh             : 1 1

That looks like a mistake of zsh to me. How could be reasonable that exporting a variable does not retain the exported value ?

But that is because the exported variable: var, is the same var that is printed.

Exporting some other var.

If the line is changed to something more similar to what you are asking, with some other varname, like this:

$ var=1; printf "%s" "$var"; var=2 export othervar; echo " $var"

The difference(s) become clear:

jsh             : 1 2
dash            : 1 2
bash            : 1 1
posixbash       : 1 2
ksh93           : 1 2
zsh             : 1 1

It is clear that bash is different to old sh (Bourne), newer sh (dash), ksh and others (not listed here).

But what is more important is that bash acts differently than bash --posix.

Posix requirement.

Some of the shell builtins (not all) are called "special built-ins":

From: 2.14. Special Built-In Utilities

The following "special built-in" utilities shall be supported in the shell command language. … however, the special built-in utilities described here differ from regular built-in utilities in two respects:

  1. An error in a special built-in utility may cause a shell executing that utility to abort, while an error in a regular built-in utility shall not cause a shell executing that utility to abort. …

  2. As described in Simple Commands, variable assignments preceding the invocation of a special built-in utility remain in effect after the built-in completes; this shall not be the case with a regular built-in or other utility.

So, in a simple command: var=2 specialBuiltin the variable var should retain its value after the specialBuiltin has exited. But not all shell implementations follow such rule.

So, this should print 1 hello 2 (as eval is an special builtin):

var=1; printf '%s ' "$var"; var=2 eval printf %s hello; echo " $var"

It does in sh and bash --posix but not in plain bash.

List of special builtins.

In fact, The POSIX list of special builtins is here, the list being:

02 break
03 :
04 .
05 continue
06 eval
07 exec
08 exit
09 export
10 readonly
11 return
12 set
13 shift
14 times
15 trap
16 unset

The number in first column is the value of var used for each test.

We could test all special builtins (except exit, exec and times) with this code:

var=01;
while : ; do var=02 break; done;    printf ' %s' "02-$var"; var=01
var=03 : ;      printf ' %s' "03-$var"; var=01
echo 'printf " %s" "04-<$var>"' >source-sh
var=04 . source-sh; printf ' %s' "04-$var"; var=01
c=0; while ((c++<1)); do
     var=05 continue
     done; printf ' %s' "05-$var"; var=01
var=06 eval 'printf " %s" "06-<$var>"'; printf ' %s' "06-$var"; var=01
#( var=07 exec bash -c  'printf " %s" "07-$var"'); var=01
#( var=08 exit;     printf ' %s' "08-$var" ); var=01 
var=09 export var;  printf ' %s' "09-$var"; var=01
var=10 readonly i;  printf ' %s' "10-$var"; var=01
varfun(){ var=11 return; }; varfun; printf ' %s' "11-$var"; var=01
var=12 set -- aa ;  printf ' %s' "12-$var"; var=01
var=13 shift;       printf ' %s' "13-$var"; var=01
var=15 trap;        printf ' %s' "14-$var"; var=01
var=16 unset j;     printf ' %s' "15-$var"; var=01
echo

To print this list:

jsh             :  02-02 03-03 04-<04> 04-04 05-05 06-<06> 06-06 09-09 10-10 11-11 12-12 13-13 15-15 16-16
dash            :  02-02 03-03 04-<04> 04-04 05-05 06-<06> 06-06 09-09 10-10 11-11 12-12 13-13 15-15 16-16
bash            :  02-01 03-01 04-<04> 04-01 05-01 06-<06> 06-01 09-09 10-01 11-01 12-01 13-01 15-01 16-01
posixbash       :  02-02 03-03 04-<04> 04-04 05-05 06-<06> 06-06 09-09 10-10 11-11 12-12 13-13 15-15 16-16
ksh93           :  02-02 03-03 04-<04> 04-04 05-05 06-<06> 06-06 09-09 10-10 11-11 12-12 13-13 15-15 16-16
zsh             :  02-01 03-01 04-<04> 04-01 05-01 06-<06> 06-01 09-01 10-01 11-01 12-01 13-01 15-01 16-01

As you can see, where posix request that var retain 02 most shells retain it and print 02-02, but not in bash (nor zsh) as they print 02-01. Only in export is bash printing 09-09 and zsh printing 09-01.

  • So you're saying that in my case bash doesn't keep the value assigned because, since the "export var" command used individually makes a variable visible in the subprocesses and since the bash with the default behavior doesn't take into account special builtins, my bash takes care of exporting the name of the variable that doesn't match with the one assigned, losing the value? – alessaro Jun 11 '18 at 7:04
  • Please do not claim to have a complete list of Bourne-alike shells if you miss important implementations. For an expressive comparison, it is sufficient to list the ones that are most important and/or well maintained. These are: bash dash bosh mksh ksh88 ksh93 zsh I am not sure wether yash should be in that list because of many deviations. zsh also has many deviations and dash cannot be used on a full blown UNIX system since it misses support for multi-byte characters. – schily Jun 11 '18 at 9:16
  • Let me add a note to the heirloom shell. It has been derived from the SunOS Bourne Shell and modified in various undocumented ways. It is not portable and it is unmaintained. In fact, there was no more than 3 months of work from the maintainer. If you like to verify the behavior of the SunOS Bourne Shell, it is better to use the "osh" binary compiled from the bosh sources, since that is very close to the SunOS original and portable. – schily Jun 11 '18 at 9:19
  • @alessaro "Your case" is not the export var case. In the later case, the first I cover, both sh and bash act exactly the same. "Your case" is the second I cover. – Isaac Jun 11 '18 at 16:25
  • @schily I am very (very) sorry for your pain. ... But the only thing that matters here is wether "the Bourne shell" prints 1 1 or 1 2, the rest are just opinions. – Isaac Jun 11 '18 at 16:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.