9

Is there any standard that covers the portability of running a command after variable assignment on the same line?

APPLE="cider" echo hi

How portable is something like that? Where will it work and where won't it?

Also: my shell scripts start with #!/bin/sh if that makes any difference.

4 Answers 4

8

As long as you're using a POSIX compliant shell, yes.

From the POSIX definition of shell command language: (relevant points in bold)

A "simple command" is a sequence of optional variable assignments and redirections, in any sequence, optionally followed by words and redirections, terminated by a control operator.

When a given simple command is required to be executed (that is, when any conditional construct such as an AND-OR list or a case statement has not bypassed the simple command), the following expansions, assignments, and redirections shall all be performed from the beginning of the command text to the end:

  1. The words that are recognized as variable assignments or redirections according to Shell Grammar Rules are saved for processing in steps 3 and 4.

  2. The words that are not variable assignments or redirections shall be expanded. If any fields remain following their expansion, the first field shall be considered the command name and remaining fields are the arguments for the command.

  3. Redirections shall be performed as described in Redirection.

  4. Each variable assignment shall be expanded for tilde expansion, parameter expansion, command substitution, arithmetic expansion, and quote removal prior to assigning the value.

In the preceding list, the order of steps 3 and 4 may be reversed for the processing of special built-in utilities; see Special Built-In Utilities.

If no command name results, variable assignments shall affect the current execution environment. Otherwise, the variable assignments shall be exported for the execution environment of the command and shall not affect the current execution environment (except for special built-ins).


Also, yes #!/bin/sh matters. From the POSIX definition of sh:

The sh utility is a command language interpreter that shall execute commands read from a command line string, the standard input, or a specified file. The application shall ensure that the commands to be executed are expressed in the language described in Shell Command Language.

So basically it says that sh must follow the rules we covered above.

So as long as you're on a POSIX compliant OS, you're good.

5
  • A small comment about your last point. While POSIX mandates sh to comply with the standard, it doesn't specify what path sh must have so /bin/sh is not necessarily the POSIX shell and is actually the legacy Bourne shell on some platforms (essentially Solaris 10 and older nowadays). This won't hurt in this particular variable assignment case as this syntax long predates the POSIX standard.
    – jlliagre
    Oct 24, 2013 at 5:42
  • It doesn't mandate that it reside at /bin/sh no. But it does mandate how sh must behave. That's all that matters. You can't stick something else called sh there and be POSIX compliant.
    – phemmer
    Oct 24, 2013 at 12:29
  • This is incorrect. A POSIX compliant operating system is free to provide a non POSIX compliant /bin/sh. What is mandatory is when using a compliant path, i.e., PATH=$(getconf PATH), running sh will call the POSIX compliant shell.
    – jlliagre
    Oct 24, 2013 at 13:01
  • Thanks Patrick. To touch on what @jlliagre said, I've read that /bin/sh may be a bourne compatible shell but not necessarily POSIX compatible? Is that true? I installed the heirloom shell (a port of the original bourne shell) and it tested fine with my example. I also tested zsh on Ubuntu just to see what would happen and I didn't get any error like in llua's example.
    – test
    Oct 24, 2013 at 17:12
  • 1
    @test As I wrote in my earlier comment, the var=x command syntax predates POSIX so there is no problem using a non POSIX still Bourne based shell in your case.
    – jlliagre
    Oct 25, 2013 at 4:33
3

To answer the second question, incompletely (the number of shells are mindboggling)

% for sh in dash ksh93 mksh rc fish bash zsh csh tcsh jsh; do
printf '%s\n' "$sh: $($sh -c 'var=foo echo hi')"
done

dash: hi
ksh93: hi
mksh: hi
rc: hi
fish: Unknown command “var=foo”. Did you mean “set var foo”? For information on assigning values to variables, see the help section on the set command by typing “help set”
Standard input: var=foo echo hi
                ^
fish:
bash: hi
zsh: hi
var=foo: Command not found.
csh:
var=foo: Command not found.
tcsh:
jsh: hi

In most modern Korn-like shells, it will work. But i don't think it is a standard, I am sure someone like Stephane Chazelas would know just how common it is.

1
  • This syntax is in use since the early days of the Bourne Shell in 1976. Note that csh is non standard and that zsh is non standard by default.
    – schily
    Aug 13, 2018 at 20:02
2

Yes, it's been this way since the early days of sh

http://www.freebsd.org/cgi/man.cgi?query=sh&apropos=0&sektion=0&manpath=Unix+Seventh+Edition&arch=default&format=html

The environment for any simple-command may be
augmented by prefixing it with  one  or more
assignments to parameters.  Thus these two lines
are equivalent

      TERM=450 cmd args
      (export TERM; TERM=450; cmd args)
2

Given the command in your example, echo will run, but what happens to $APPLE is a little more complicated.

It is true, as is indicated in @Patrick's answer here, that if the shell invokes a process all variables declared on the command-line preceding its invocation are specified to be exported into its environment. And further, those variables are also specified to expire with the invoked process - so...

unset var; var=val cmd; echo ${var-unset.}

...should print unset.

But where this whole concept gets a little more complicated though, and as your own example demonstrates, is at the point that cmd is not an invoked process but is instead either a shell-builtin, a shell function, or a special shell builtin utility. In every one of those three cases the shell will most likely just run some of its own routines in memory, and invoke nothing at all.

For example, echo is almost definitely a shell builtin utility - as most shells I know of provide it as such - but it is not a POSIX-specified special builtin. In this way, it is basically a shell function that must emulate an outside executable. This is probably stated a little more clearly here:

The term "built-in" implies that the shell can execute the utility directly and does not need to search for it. An implementation may choose to make any utility a built-in; however, the special built-in utilities described here differ from regular built-in utilities...

Variable assignments specified with special built-in utilities remain in effect after the built-in completes; this shall not be the case with a regular built-in or other utility.

The special built-in utilities in this section need not be provided in a manner accessible via the exec family of functions defined in the System Interfaces volume of POSIX.1-2008.

So a variable declared on echo's command-line perishes with echo, but one declared on set's command-line persists - (though bash by default violates this rule). The same holds true when cmd is a shell function:

When a function is executed, it shall have the syntax-error and variable-assignment properties described for special built-in utilities in the enumerated list at the beginning of Special Built-In Utilities.

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .