4

Let's say we have a command output a variable assignment string, say 'var=foo', if I put this command in Command Substitution which looks like $(echo var=foo), it causes 'command not found' error.

[223212 dot@anne ~]$ var=foo
[223226 dot@anne ~]$
[223230 dot@anne ~]$ $(var=foo)
[223235 dot@anne ~]$
[223236 dot@anne ~]$ $(echo var=foo)
bash: var=foo: command not found
[223240 dot@anne ~]$
[224909 dot@anne ~]$ $(echo ls)
a    b    c   d    
[225036 dot@anne ~]$
[225110 dot@anne ~]$ $(echo $(var=foo))
[225116 dot@anne ~]$

Since we can directly put variable assignment in Command Substitution like this $(var=foo) (though it's meaningless I think), and $(echo ls) works as expected as well, why output a assignment in Command Substitution causing error?

This is man bash about Command Substitution:

Command substitution allows the output of a command to replace the command name.

Bash performs the expansion by executing command in a subshell environment and replacing the command substitution with the standard output of the command, with any trailing newlines deleted.

As I understand it, $(echo var=foo) should be replace by var=foo just like $(var=foo).

Am I getting it wrong?

7
  • when you use command substitution you should assign the output to something, i.e. foo=$(echo buzz). Of course "echo" doesn't make such sense here, it doesn't require a command substitution usually, you would normally just use foo=buzz Commented Mar 23, 2018 at 15:32
  • 1
    That's interesting, it looks like Bash considers the output of the subprocess to be a command name, not an other instruction. It's like if you had typed var\=foo, thus the command not found error. Try to eval the result to fix this: eval $(echo var=foo). Commented Mar 23, 2018 at 15:34
  • I know it doesn't make sense, and I know may be I can use eval to sovle problem like this. But that's not the point. Using echo is just an easy example, actually I'm using $(gpg -qd file). Commented Mar 23, 2018 at 15:36
  • 1
    If you're using $(pgp -qd file), then you're presumably running into the issues described in detail in BashFAQ #50 (as a command generated from an unquoted command substitution has the exact same caveats as one generated via the unquoted expansion of a string). Commented Mar 23, 2018 at 15:40
  • 1
    @Pawamoy, nothing surprising about it -- the only parse stages unquoted expansion results go through is string-splitting and globbing; assignments are recognized before that point. eval may be well what the OP wants, but it's dangerous and best not recommended without explicit caveats. Commented Mar 23, 2018 at 15:54

1 Answer 1

6

Here's man bash:

SIMPLE COMMAND EXPANSION
   When a simple command is executed, the shell performs the fol‐
   lowing expansions, assignments, and redirections, from left to
   right.

   1.     The  words  that  the  parser  has  marked  as variable
          assignments (those  preceding  the  command  name)  and
          redirections are saved for later processing.

   2.     The words that are not variable assignments or redirec‐
          tions are expanded.  If any words remain  after  expan‐
          sion,  the  first  word  is taken to be the name of the
          command and the remaining words are the arguments.
   [...]

In your case, the simple command has a single word $(echo var=foo).

Since there are no words marked as variable assignments (because this word is instead a command substitution), step 1 doesn't apply.

We then move on to step 2, where the word $(echo var=foo) is expanded into var=foo. We don't go back to the first step, we just do what step 2 says: "take the first word as the name of the command".

This is why var=foo is executed as a command instead of being interpreted as an assignment.

Sign up to request clarification or add additional context in comments.

14 Comments

If so, how about $(var=foo)? $(var=foo) is a single word and obviously is not going to be marked as variable assignments as well, then to step 2...
@dotc, var=foo has no output when it's run, so $(var=foo) is equivalent to $(), which does nothing.
@haccks There are two words in echo var=foo, but there is only one word in $(echo var=foo) (a single command substitution, regardless of what it contains).
@haccks $(echo var=foo) will become var=foo. It will not become $(var=foo). echo var=foo outputs var=foo, but it isn't replaced with var=foo in any sense.
@haccks $(echo var=foo) is a simple command with a single word. In step 1, we check for assignments and redirections. $(echo var=foo) is not an assignment or redirection, it's just a command substitution. Then we move on to step 2. As part of expansion, we run echo var=foo, capture its result (the string var=foo), and substitute it. We now have the word var=foo. However, we don't start from the top and begin parsing this as an assignment. We're still in step 2. Step 2 says that "if any words remain [..], the first word is taken to be the name of the command", so we run it as a cmd
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.