34

I have picked up -- probably on Usenet in the mid-1990s (!) -- that the construct

export var=value

is a Bashism, and that the portable expression is

var=value
export var

I have been advocating this for years, but recently, somebody challenged me about it, and I really cannot find any documentation to back up what used to be a solid belief of mine.

Googling for "export: command not found" does not seem to bring up any cases where somebody actually had this problem, so even if it's genuine, I guess it's not very common.

(The hits I get seem to be newbies who copy/pasted punctuation, and ended up with 'export: command not found or some such, or trying to use export with sudo; and newbie csh users trying to use Bourne shell syntax.)

I can certainly tell that it works on OS X, and on various Linux distros, including the ones where sh is dash.

sh$ export var=value
sh$ echo "$var"
value
sh$ sh -c 'echo "$var"'  # see that it really is exported
value

In today's world, is it safe to say that export var=value is safe to use?

I'd like to understand what the consequences are. If it's not portable to v7 "Bourne classic", that's hardly more than trivia. If there are production systems where the shell really cannot cope with this syntax, that would be useful to know.

tripleee
  • 7,699
  • 2
    thanks I finally understood why I see so often the thing that I thought was useless: var=value;export var – Thorsten Staerk Mar 28 '15 at 15:56
  • 3
    There's still a few Solaris boxes kicking around, and those are notoriously frugal in their standard tools; on the other end of the spectrum, doesn't busybox come with its own minimal shell? (I am not in a position to try either right this second.) – Ulrich Schwarz Mar 28 '15 at 16:07
  • Thanks Ulrich, Solaris may well be the culprit why this long syntax is still around. – Thorsten Staerk Mar 28 '15 at 16:09

2 Answers2

32

It is not a bashism but a POSIX compliant syntax. It actually started as a kshism quite a long time ago and was later adopted by almost all Bourne syntax based shells. The only notorious exception is /bin/sh on Solaris 10 and older which sticks to the legacy Bourne shell syntax. Hopefully, Solaris 11 uses a POSIX compliant shell as /bin/sh.

By the way, export was already a builtin command in the legacy Bourne shell so googling for export: command not found was misleading.

Here is the legacy Bourne shell behavior when export is combined with an assignment:

$ export var=22
var=22: is not an identifier

For the nostalgics, the source code of this original Bourne shell is available and can be compiled for most Unix and Linux distributions.

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
jlliagre
  • 61,204
  • Many thanks for the historical insight and the correct error message to google for! Obvious in retrospect, blush... – tripleee Mar 28 '15 at 17:31
  • 5
    That's not the source code of the original Bourne shell, that's a modified OpenSolaris sh. It is the Bourne shell but after having undergone decades of evolution. The original Bourne shell, as shipped with Unix V7 can be found at the Unix Heritage Society – Stéphane Chazelas Mar 28 '15 at 21:34
  • 1
    @StéphaneChazelas Strictly speaking, you are as usual correct. Note however that I didn't wrote "the original Bourne shell" but "this original Bourne shell" as I was referring to the shell used by Solaris 10 and its source code that can be compiled on modern platforms. Note also that the Bourne shell had several features added between 1977 and 1989 but then essentially ceased to evolve (outside porting/adapting to newer platforms and bug fixes) in the last 25 years or so. – jlliagre Mar 28 '15 at 22:00
  • @jeffSchaller Thanks for correcting affectation, a false friend as it means "assignment" in French. On the other hand, to Google is controversial – jlliagre Jan 24 '22 at 15:25
  • 1
    Ahhh, I haven't learned enough French to recognize affectation; thanks for the background! I've also just learned about the xerox effect when it comes to verbs, so I'm happy to revert that change. – Jeff Schaller Jan 24 '22 at 15:28
24
export foo=bar

was not supported by the Bourne shell (an old shell from the 70s from which modern sh implementations like ash/bash/ksh/yash/zsh derive). That was introduced by ksh.

In the Bourne shell, you'd do:

foo=bar export foo

or:

foo=bar; export foo

or with set -k:

export foo foo=bar

Now, the behaviour of:

export foo=bar

varies from shell to shell.

The problem is that assignments and simple command arguments are parsed and interpreted differently.

The foo=bar above is interpreted by some shells as a command argument and by others as an assignment (sometimes).

For instance,

a='b c'
export d=$a

is interpreted as:

'export' 'd=b' 'c'

with some shells (ash, older versions of zsh (in sh emulation), yash) and:

'export' 'd=b c'

in the others (bash, ksh).

While

export \d=$a

or

var=d
export $var=$a

would be interpreted the same in all shells (as 'export' 'd=b' 'c') because that backslash or dollar sign stops those shells that support it to consider those arguments as assignments.

If export itself is quoted or the result of some expansion (even in part), depending on the shell, it would also stop receiving the special treatment.

See Are quotes needed for local variable assignment? for more details on that.

The Bourne syntax though:

d=$a; export d

is interpreted the same by all shells without ambiguity (d=$a export d would also work in the Bourne shell and POSIX compliant shells but not in recent versions of zsh unless in sh emulation).

It can get a lot worse than that. See for instance that recent discussion about bash when arrays are involved.

(IMO, it was a mistake to introduce that feature).

Kusalananda
  • 333,661
  • I was surprised that the semicolon is not required in foo=bar export foo, as I had always seen it there. I know export is a builtin, but why does foo=bar; foo=baz export foo; echo $foo behave differently than foo=bar; foo=baz /bin/cat /dev/null; echo $foo? – jrw32982 Apr 01 '15 at 14:23
  • 4
    @jrw32982, because it's a builtin. You still get that in modern POSIX shells but only for special builtins which export is. – Stéphane Chazelas Apr 01 '15 at 14:26
  • Although it discusses declare, not export, I recommend that anyone who cares about security read the discussion at the link that StéphaneChazelas provided to bash.bugs. – John1024 Nov 26 '15 at 00:14
  • Great answer! But it took a long time to get to d=$a export d is interpreted the same by all shells without ambiguity ;-) – conny Jul 03 '18 at 11:12
  • @conny, d=$a export d doesn't work anymore in zsh, so I've updated the answer. See edit. – Stéphane Chazelas Jul 03 '18 at 13:19
  • +1. Also, your answer suggests this but I think it would be better to be more clear at the beginning that in general foo=bar export foo is not portable, and teaches an even more generally un-portable habit. There are shells for which foo=bar abuiltin does not set foo as one would expect. This is the kind of portability detail I would normally ignore but if we're talking about the kind of portability where not doing export foo=bar matters, then this also matters. – mtraceur May 01 '19 at 04:07
  • @mtraceur, foo=bar export foo is Bourne and POSIX (as export is a so-called special builtin). Do you know of any shell beside zsh (when not in sh emulation, except in a few versions) where it doesn't work? – Stéphane Chazelas May 01 '19 at 07:17
  • @StéphaneChazelas My main point is more general, about the portability of the pattern as humans will learn it: most people who see foo=bar export foo without disclaimers will eventually try to expand it to foo=bar some command, which will generally seem to work right in most common uses so they will come to trust it, and then they will be bitten by portability problems when some command turns out to be one of the exceptions that doesn't work portably. – mtraceur May 02 '19 at 20:18
  • @StéphaneChazelas I have seen claims that foo=bar some_builtin is not portable to all shells for all builtins, but I don't know which builtins that was referring to. Since you can't think of any other exception for export specifically, and I trust you as somewhat of a domain expert in the field of shell script portability, I guess I mostly trust foo=bar export foo to portably put foo into the environment with the value bar, and I guess I sorta trust that all shells will then portably expand $foo to the same value in subsequent commands. – mtraceur May 02 '19 at 20:33