37

Here is my small bash script snippet.

i=5
command='echo $i'
$command

I want this script to print 5 i.e., I want it to run echo and print 5. But it instead keeps printing $i. So how do I go about solving this?

posixKing
  • 1,077
  • 2
  • 12
  • 13
  • 2
    Possible duplicate of [What is the "eval" command in bash?](http://unix.stackexchange.com/questions/23111/what-is-the-eval-command-in-bash) – Michael Homer Apr 07 '17 at 09:09
  • 4
    I voted to leave this open because the duplicate question is much broader while the advice to use a function in Stéphane's answer (upvoted) is appropriate for this specific question. – Anthony Geoghegan Apr 07 '17 at 10:34
  • 1
    In your case it is probably simpler to define your command as alias, eg. alias mycommand='echo $i' and then simply type mycommand. – dhm Jan 11 '23 at 11:43

1 Answers1

58

That would be:

eval "$command"

If you want the content of $command to be evaluated as shell code.

If you can't guarantee that $command won't start with - (which would cause eval in some shells like bash to treat it as an option), you may want to run:

eval " $command"

instead. That extra leading space won't affect the way the command is parsed and will prevent $command from being treated as an option to eval if it starts with -. eval -- "$command" would also work in some shells (including bash) but is not POSIX (IIRC) and wouldn't work in dash or the Bourne shell for instance.

Note that your $command should probably be:

command='echo "$i"'

Unless you did intend $i to be subject to split+glob

A potentially better way to store code in "variables" would be to use functions:

mycommand() { echo "$i"; }

(using mycommand instead of command, as command is already an existing command).

If $command is break/continue/return, behaviour will vary depending on the shell.

If you wanted $command to store a simple command, that is a list of words the first of which is looked up as the command to execute with the list of words as arguments, you'd use an array variable:

command=('echo' '$i' "$i")
"${command[@]}"

That would run echo, with echo, $i and the content of $i as arguments.

command='echo $i ;x /* '$i
$command

(with the default value of $IFS) is the one that would make littlest sense. There, $command would contain a string. The first $i left as is, the second expanded (as outside the single quotes), and then that string would be subject to split+glob (as $command is not inside double quotes), the outcome of which would result in a number of words, taken as a simple command again.

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
  • Wow thanks. I knew it was eval, but I was missing double quotes. – posixKing Apr 07 '17 at 09:09
  • 1
    Im guessing because you first suggested `eval "$command"` rather than just suggesting to change the quotes to `"`, i.e. `i=5; command="echo $i"; $command` which also would have worked, you prefer the `eval` method? personally I find using `bash -c "$command"` to be more reliable - assuming you dont need the command to be executed in current process – the_velour_fog Apr 07 '17 at 09:29
  • @the_velour_fog, `command="echo $i"; $command` is not the same thing. That's expanding `$i` into the value of `$command` and then apply split+glob to `$command`. That wouldn't work if `$IFS` was modified, that wouldn't work for `command='cmd1; cmd2'`, etc. I can't see the point of using `bash -c "$command"` unless you do want that command to run in a separate `bash` instance. Here that wouldn't work unless you _exported_ `$i`. – Stéphane Chazelas Apr 07 '17 at 09:34
  • 1
    ah ok, after re-reading my comment, I didnt explain very well, I meant I find `bash -c "$command"` more reliable than `$command` for the reasons you mentioned. the way I would use the commands is `bash -c "$command"` when you need to run as a separate instance, e.g. `sudo -u $SUDO_USER bash -c "$command"` , `eval "$command"` when you want to run in - and affect - the current process . – the_velour_fog Apr 07 '17 at 09:56
  • the suggestion to use a function instead was a huge help. thanks – FireDragon Jul 27 '18 at 00:27
  • But if `$command` has the following value: `VAR1=foo VAR2=Lo23>-f`, then `echo $VAR2` displays `Lo23`. Any fix to that? – payne Dec 02 '21 at 19:00
  • 1
    @payne `>` is a redirection operator in the syntax of the shell, so you'd need it to be quoted if you want it to be part of the string that is assigned to the `VAR2` variable: `$command` should contain `VAR1=foo VAR2='Lo23>-f'` for instance. – Stéphane Chazelas Dec 02 '21 at 20:09