Execute a command stored in a variable

Short answer: see BashAQ #50:I'm trying to put a command in a variable, but the complex cases always fail!.

Long answer: it's because of the order in which bash parses the command line. Specifically, it looks for things like pipes and redirects before it expands variable values, and doesn't go back and re-look for pipes etc in the expanded values. Essentially, variables get substituted about halfway through the parsing process, so the value only gets halfway-parsed before being executed.

In order to solve this, you need to answer @slhck's question: why is the command stored in a variable in the first place? What is the actual problem you're trying to solve? Depending on the actual goal, there are a number of possible solutions:

  • Don't store it in a variable, just execute it directly. Storing commands for later use is tricky, and if you don't really need to, just don't.

  • Use a function instead of a variable. That's what they're for, after all:

    i() { cat -nT index.php |grep 'someregex'; }
    i
    

    The main disadvantage of this is that you can't create the function dynamically -- you can't conditionally include or exclude code from the function (although the function itself can have conditional elements that're selected when it's executed).

  • Use eval. This should be considered a last resort, since it's easy to get unexpected behavior. It essentially runs the command through another full parsing pass, so all pipes etc get their full meaning -- but it also means that parts of the command you thought were just data will also get parsed and maybe executed. Filenames that contain shell metacharacters (pipe, semicolon, quote/apostrophe, etc) can have weird and sometimes dangerous effects. If you do use eval, at least double-quote the string, otherwise its contents essentially get parsed one-and-a-half times, with even weirder results.

    i="cat -nT index.php |grep 'someregex'"
    eval "$i"
    

EDIT: Another standard store-a-command-in-a-variable approach is to use an array instead of a simple variable. This allows you store a command with complex arguments (e.g. containing spaces) without trouble, but won't store things like pipes and redirects. So, the array approach won't be useful in this particular case.


This should just work:

a="cat -nT index.php |grep 'someregex'"
eval "$a"

Beware that eval introduces potential vulnerabilities and unexpected behavior situations so must be used, if ever, with extra care.


When declaring the variable, you shouldn't use the dollar sign as a prefix.

Try this:

i='echo hi'; $i