Bash: Why use eval with variable expansion?

From FVue
Jump to: navigation, search

Contents

Problem

I don't understand why I have to use eval:

#!/bin/bash
cmd="date --date=\"1 days ago\""
$cmd         # Doesn't work
eval $cmd    # Works

Solution

Before the shell executes a command, it performs the following operations (check the manual for details):

  1. Syntax analysis (Parsing)
  2. Brace expansion
  3. Tilde expansion
  4. Parameter and variable expansion
  5. Command substitution
  6. Arithmetic expansion
  7. Word splitting
  8. Filename expansion
  9. Quote removal

It is important to realize that parsing takes place before parameter and command substitution. The result of a step n is subject to the next steps (n+), but the preceding steps (n-) are not re-executed. In other words: after for example variable expansion (step 4) the result is not re-parsed (step 1).

In the example of the command:

$cmd   # cmd="date --date=\"1 days ago\""

the command date --date=\"1 days ago\" is the result of variable expansion (step 4). Quote removal (step 9) removes the backslashes and turns the command into date --date="1 days ago". But since parsing (step 1) already has taken place, "1 days ago" is not seen as 1 argument but as 3 separate arguments: "1, days and ago", which gives you the error message:

date: extra operand `ago"'
Try `date --help' for more information.

To force one more run through the parsing/expansion procedure, use:

eval $cmd

Now first $cmd is expanded (steps 1-9) to date --date="1 days ago". Then eval causes the expanded string to be re-parsed again (steps 1-9), resulting in the correct command:

date --date="1 days ago"
Wed Nov 29 08:11:46 CET 2006

See also

http://www.mpi-sb.mpg.de/~uwe/lehre/unixffb/quoting-guide.html#para:sh-misconceptions
Uwe Waldmann - A Guide to Unix Shell Quoting - Common misconceptions
http://www.linuxquestions.org/questions/showthread.php?p=2523172#post2523172
Bash Command Substitution - LinuxQuestions.org

Comments

blog comments powered by Disqus

Personal tools
Google