29

One of my coworkers has provided me with a Bash syntax that I am unfamiliar with. My Google foo has failed me on figuring out what it does and why/when I should use it.

The command that he sent me was of this form:

someVariable=something command

Initially, I thought that this was equivalent to the following:

someVariable=something ; command

Or

someVariable=something 
command

But this doesn't appear to the be case. Examples:

[Jan-03 11:26][~]$ # Look at the environment variable BAZ. It is currently empty
[Jan-03 11:26][~]$ echo $BAZ

[Jan-03 11:27][~]$ # Try running a command of the same format    
[Jan-03 11:27][~]$ BAZ=jake echo $BAZ

[Jan-03 11:27][~]$    
[Jan-03 11:27][~]$ # Now, echo BAZ again. It is still empty:    
[Jan-03 11:27][~]$ echo $BAZ

[Jan-03 11:27][~]$    
[Jan-03 11:28][~]$    
[Jan-03 11:28][~]$ # If we add a semi-colon to the command, we get dramatically different results:
[Jan-03 11:28][~]$ BAZ=jake ; echo $BAZ
jake
[Jan-03 11:28][~]$
[Jan-03 11:28][~]$ # And we can see that the variable is actually set:
[Jan-03 11:29][~]$ echo $BAZ
jake
[Jan-03 11:29][~]$

What does this syntax do? What happens to the variable that has been set? Why does this work?

ilkkachu
  • 138,973
  • 2
    Google isn't your best friend and never will be. When it comes to learning linux/bash command line man pages are your best friend :-) You might wanna make it a habit to read them often. – Alex Jones Jan 14 '17 at 07:00
  • 2
    @edwardtorvalds - If you had the same question (what is this syntax), how would you find it in the man pages? – sixtyfootersdude Jan 16 '17 at 14:30
  • 1
    you don't usually find solutions to your problem in man pages, of course. But instead you read all man pages of commands you use and thus prevent such problem before they are created :-) The solution you have accepted quotes answer to your problem from man page of bash – Alex Jones Jan 17 '17 at 08:43
  • 2
    "you read all man pages of commands you use". C'mon dude, that's like reading all the fine print for every item you buy or use. Great advice in theory but 100% impractical in practice. If I don't know x today I'm probably not going to spend hours reading all the man pages so I am 100% knowledeable about everything (only in my own mind is that true of course). I'm probably going to post my specific question to a site that supports it and now my mind is free to store other stuff – Michael Durrant Jan 28 '17 at 15:11

4 Answers4

40

This is equivalent to:

( export someVariable=something; command )

This makes someVariable an environment variable, with the assigned value, but only for the command being run.

Here are the relevant parts of the bash manual:

Simple Commands

A simple command is a sequence of optional variable assignments followed by blank-separated words and redirections, and terminated by a control operator. The first word specifies the command to be executed, and is passed as argument zero. The remaining words are passed as arguments to the invoked command.

(...)

Simple Command Expansion

If no command name results [from command expansion], the variable assignments affect the current shell environment. Otherwise, the variables are added to the environment of the executed command and do not affect the current shell environment.

Note: bear in mind that this is not specific to bash, but specified by POSIX.


Edit - Summarized discussion from comments in the answer

The reason BAZ=JAKE echo $BAZ, doesn't print JAKE is because variable substitution is done before anything else. If you by-pass variable substitution, this behaves as expected:

$ echo_baz() { echo "[$BAZ]"; }
$ BAZ=Jake echo_baz
[Jake]
$ echo_baz
[]
xhienne
  • 17,793
  • 2
  • 53
  • 69
19

These are variable assignments in the context of simple commands. As xhienne has mentioned, for external commands they are equivalent to exporting the assigned value(s) for the duration of the command.

In your examples you're using a built-in command, so the behaviour isn't quite the same: the assignments affect the current environment, but whether the effect lasts after the execution of the built-in command is unspecified. To understand your examples, you need to know that parameter expansion happens before variables are handled; thus with

 BAZ=jake echo $BAZ

the shell first expands $BAZ (resulting in nothing), then sets BAZ to jake, and finally runs

 echo

which prints an empty line. (Then the shell forgets BAZ as your subsequent echo $BAZ shows.)

 BAZ=jake; echo $BAZ

is interpreted as two commands: first the BAZ variable is set in the current environment, then echo $BAZ is expanded to echo jake and executed.

Stephen Kitt
  • 434,908
5

There are a few key things that happen here:

  • As explained in bash reference manual, Simple Command Expansion, section "If no command name results, the variable assignments affect the current shell environment. Otherwise, the variables are added to the environment of the executed command and do not affect the current shell environment." Thus, when you say var="something" command arg1 arg2, the command will run with var being in command's environment and disappearing after command exits. Demonstration of this is simple - access the command's environment from within the command:

    $ BAZ="jake" python -c "import os; print os.environ['BAZ']"                                                              
    jake
    

    This is nothing to be surprised about. Such syntax is frequently used to run programs with modified environment. My fellow unix.stackexchange.com and askubuntu.com users will recognize this example: if user uses German locale, and you only speak in English, you can ask them to reproduce whatever issue they're having and obtain output in English like so:

    LC_ALL=C command
    

    Note also that environment access isn't specific to python. It can be done with C or any other programming language. It just so happened to be my "weapon of choice" right now and only used for this demo.

  • The variable expansion occurs before anything runs. Thus, when you run something like BAZ="foo" echo $BAZ, the shell looks into its environment first, finds no variable BAZ there , and thus leaves $BAZ empty. Simple demonstration of that is:

    $ BAZ="jake" python -c "import sys; print 'ARG:',sys.argv[1]" $BAZ                                                       
    ARG:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
    IndexError: list index out of range
    

It's important to also note, the distinction from BAZ=jake; echo $BAZ which is two separate command statements and here BAZ=jake will stay in shell's environment. The use case depends on your intentions. If you intend running more than one program that needs such variable, it might be desirable to export such variable. If you only need it for this one specific time, then preceding variable assignments are might be preferable.

terdon
  • 242,166
3

In simple words:

BAZ=jake echo $BAZ

doesn't output anything because, variable substitution takes place first in a command. Which means that, the first thing that happens on this command line is $BAZ will be replaced with whatever actual value that variable BAZ was defined with, earlier (if any). Mind that this happens even before shell considers BAZ=jake on the same command line.

Prior to execution of our command, since BAZ doesn't have any value assigned and since BAZ=jake is considered only after $BAZ is resolved, $BAZ doesn't resolve to any value. And hence echo $BAZ doesn't output anything.

BAZ=jake is only part of the given command (shell will not consider/set it as an environment variable). This is mostly useful, if some process executed as part of the same command line, uses this variable BAZ. The value of BAZ, jake is volatile once the command execution is completed.

For example: ]# LD_LIBRARY_PATH="new_path" ldconfig, where ldconfig command internally refers to variable LD_LIBRARY_PATH and there is no variable expansion in this command line unlike above.

For other case:

BAZ=jake; echo $BAZ  

They are two different commands, given in a single line. Its as good as executing one after other.

VanagaS
  • 774