1

For each of two examples below I'll try to explain what result I expected and what I got instead. I'm hoping for you to help me understand why I was wrong.

1)

VAR1=VAR2 $VAR1=FOO

result: -bash: VAR2=FOO: command not found

In the second line, $VAR1 gets expanded to VAR2, but why does Bash interpret the resulting VAR2=FOO as a command name rather than a variable assignment?

2) 'VAR=FOO'

result: -bash: VAR=FOO: command not found

Why do the quotes make Bash treat the variable assignment as a command name?

Could you please describe, step by step, how Bash processes my two examples?

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Peter
  • 313
  • 1
  • 3
  • 10
  • 4
    [Expansion](http://wiki.bash-hackers.org/syntax/expansion/intro) is the difference. – cxw Jan 20 '17 at 21:41
  • 2
    BTW, what you're trying to do here is discussed in depth in [BashFAQ #6](http://mywiki.wooledge.org/BashFAQ/006), particularly the [subsection on indirect assignment](http://mywiki.wooledge.org/BashFAQ/006#Assigning_indirect.2Freference_variables). – Charles Duffy Jan 20 '17 at 22:25
  • Possible duplicate of [Indirect variable assignment in bash](http://stackoverflow.com/questions/9938649/indirect-variable-assignment-in-bash) – dawg Jan 20 '17 at 22:28
  • ...could also be a duplicate of [Saving function output into a variable named in an argument](http://stackoverflow.com/questions/41450156/saving-function-output-into-a-variable-named-in-an-argument), if we read this nonliterally (as actually being "how do I do X?", vs the OP's literal question, of "why did Y and Z fail?") – Charles Duffy Jan 20 '17 at 22:29
  • By the way -- while not quite directly and literally on-point here, some resources you might find useful: The [BashParser](http://mywiki.wooledge.org/BashParser) page from the Wooledge wiki; http://shellcheck.net/ (which offers appropriate advice behind the link when this bug is detected); and [BashFAQ #50](http://mywiki.wooledge.org/BashFAQ/050), which addresses other issues stemming from misunderstanding the boundaries between data and code in bash. – Charles Duffy Jan 20 '17 at 22:53

3 Answers3

5

How best to indirectly assign variables is adequately answered in other Q&A entries in this knowledgebase. Among those:

If that's what you actually intend to ask, then this question should be closed as a duplicate. I'm going to make a contrary assumption and focus on the literal question -- why your other approaches failed -- below.


What does the POSIX sh language specify as a valid assignment? Why does $var1=foo or 'var=foo' fail?

Background: On the POSIX sh specification

The POSIX shell command language specification is very specific about what constitutes an assignment, as quoted below:


4.21 Variable Assignment

In the shell command language, a word consisting of the following parts:

varname=value

When used in a context where assignment is defined to occur and at no other time, the value (representing a word or field) shall be assigned as the value of the variable denoted by varname.

The varname and value parts shall meet the requirements for a name and a word, respectively, except that they are delimited by the embedded unquoted equals-sign, in addition to other delimiters.

Also, from section 2.9.1, on Simple Commands, with emphasis added:

  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.

Also, from the grammar:

If all the characters preceding '=' form a valid name (see the Base Definitions volume of IEEE Std 1003.1-2001, Section 3.230, Name), the token ASSIGNMENT_WORD shall be returned. (Quoted characters cannot participate in forming a valid name.)


Note from this:

  • The command must be recognized as an assignment at the very beginning of the parsing sequence, before any expansions (or quote removal!) have taken place.
  • The name must be a valid name. Literal quotes are not part of a valid variable name.
  • The equals sign must be unquoted. In your second example, the entire string was quoted.
  • Assignments are recognized before tilde expansion, parameter expansion, command substitution, etc.

Why $var1=foo fails to act as an assignment

As given in the grammar, all characters before the = in an assignment must be valid characters within a variable name for an assignment to be recognized. $ is not a valid character in a name. Because assignments are recognized in step 1 of simple command processing, before expansion takes place, the literal text $var1, not the value of that variable, is used for this matching.

Why 'var=foo' fails to act as an assignment

First, all characters before the = must be valid in variable names, and ' is not valid in a variable name.

Second, an assignment is only recognized if the = is not quoted.

Community
  • 1
  • 1
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
4

1)

VAR1=VAR2
$VAR1=FOO

You want to use a variable name contained in a variable for the assignment. Bash syntax does not allow this. However, there is an easy workaround :

VAR1=VAR2
declare "$VAR1"=FOO

It works with local and export too.

2)

By using single quotes (double quotes would yield the same result), you are telling Bash that what is inside is a string and to treat it as a single entity. Since it is the first item on the line, Bash tries to find an alias, or shell builtin, or an executable file in its PATH, that would be named VAR=FOO. Not finding it, it tells you there is no such command.

An assignment is not a normal command. To perform an assignment contained in a quote, you would need to use eval, like so :

eval "$VAR1=FOO" # But please don't do that in real life

Most experienced bash programmers would probably tell you to avoid eval, as it has serious drawbacks, and I am giving it as an example just to recommend against its use : while in the example above it would not involve any security risk or error potential because the value of VAR1 is known and safe, there are many cases where an arbitrary (i.e. user-supplied) value could cause a crash or unexpected behavior. Quoting inside an eval statement is also more difficult and reduces readability.

Fred
  • 6,590
  • 9
  • 20
  • 1
    There's nothing wrong with `eval`. Stop spreading FUD. The problem associated with `eval` is that newbies will usually use it (dangerously, as they don't grasp all the rules the shell uses) as a workaround around bad design, or because they want to mimic some patterns possible in some (more evolved) languages but impossible in shell. – gniourf_gniourf Jan 20 '17 at 22:05
  • 2
    Any dangerous device (gun, nuclear bomb, arsenic) is perfectly safe when used carefully by a professional. Yet, I would advise most people to avoid handling guns, nuclear bombs and arsenic without calling that FUD. "goto" was at some point considered harmful, but it was not because it had no safe use. So agreed, there is nothing "wrong" with eval (or guns, nuclear bombs and arsenic) as they all are what they are and do what they are built to do, but it is a shell tool that tends to be riskier than others. I sure try to avoid it (because of experience, not fear). – Fred Jan 20 '17 at 22:14
  • I mentioned FUD because your post exactly sounds like FUD-spreading: you don't even say what _serious drawbacks_ the line `eval "$VAR1=FOO"` has. – gniourf_gniourf Jan 20 '17 at 22:17
  • Updated the post to avoid giving the impression that the example itself is dangerous or that "eval" is automatically risky in all cases. – Fred Jan 20 '17 at 22:23
  • 1
    You should also quote the expansion of `VAR1` in the line `declare $VAR1=FOO`: `declare "$VAR1=FOO"`. Note that this line is also dangerous if used with arbitrary value and is subject to arbitrary code execution: `VAR1='a[0$(ls >&2)]'`. In fact, one main bad design in shell scripting is when data is used as code. – gniourf_gniourf Jan 20 '17 at 22:28
  • Assigning a constant value on the right-hand side means we're missing an opportunity to teach one of the important safeties when using the `eval` idiom: Ensuring that any expansions in the value side are performed indirectly as well. That is, `eval "$var=\$value"` is only vulnerable to malicious values in `var`, whereas `eval "$var=$value"` is vulnerable to malicious values in either. – Charles Duffy Jan 20 '17 at 22:56
  • BTW, if you wanted a canonical reference to use when warning folks to be cautious around `eval`, [BashFAQ #48](http://mywiki.wooledge.org/BashFAQ/048) is a good one. – Charles Duffy Jan 20 '17 at 22:58
  • @gniourf_gniourf added quoting as suggested. You are right about the risk if the variable name is not guaranteed safe, of course. I think in most real-life cases the data that could be outside the programmer's control would be on the value side of the assignment, so using "declare" or "local" would still be easier to deal with than eval. – Fred Jan 20 '17 at 23:50
  • 1
    Note also that *everything* in `bash` is just a string. The key difference in #2 is that the `=` is quoted, preventing it from indicating that the word is an assignment. `VAR1\=VAR2` would equally be treated as a command name rather than an assignment. – chepner Jan 21 '17 at 02:43
-1

You declare VAR2 earlier in the program, right?

If you are trying to assign the value of VAR2 to VAR1, then you need to make sure and use $ in front of VAR2, like so:

VAR1=$VAR2

That will set the value of VAR2 equal to VAR1, because when you utilize the $, you are saying that value that is stored in the variable. Otherwise it doesn't recognize it as a variable.

Basically, a variable that doesn't have a $ in front of it will be interpreted as a command. Any word will. That's why we have the $ to clarify "hey this is a variable".

  • 2
    you say "That will set the value of VAR2 equal to VAR1", and it is not true, it is the other way around. I really think the OP is trying to perform an indirect assignment (i.e. assigne value "FOO" to variable VAR2 when VAR2 is the value of another variable). – Fred Jan 20 '17 at 21:47
  • While I was trying to perform an indirect assignment (in my first example), that was not my main goal here. What I really want is to fully understand how the shell processes input, the right order of all operations etc. – Peter Jan 21 '17 at 00:45