I have a command stored in a variable. Let's pretend the variable $i has the value:

cat -nT index.php |grep 'someregex'

When I try to execute the above variable by typing $i it fails because the shell attempts to execute the entire variable as one command. I also tried using eval($i) and putting $i in backticks.

How can I make the shell execute $i as if it were a command? And why isn't it working the same as.

$i='echo hi'; $i

Is it because I had to sort of hack in the single quotes? (Because you can't nest them.) Currently my solution is

echo $i > /foo;  . /foo

But I don't want to have create a file just for this, only to delete it later.

What I mean by "I hacked in the single quotes is I did:

$i='cat index.php | grep -P '"'"'MYREGEXHERE'"'"
  • 4
    The question is, why is the command stored in a variable in the first place? You'd be better off assembling the command when you run it, storing the option arguments in variables or in an array. Can you show the entire script you have? – slhck Nov 24 '13 at 8:49
  • Same root cause: superuser.com/questions/360966/… – Ciro Santilli TRUMP BAN IS BAD Jul 5 '15 at 8:39

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'; }

    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.

  • +1 for the eval method. This stopped me from asking the same question :). I have something like sudo rsync -aAHXi -n --delete-excluded --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/lost+found","/mnt/DATA/*","/var/log/*","/var/swap","/var/cache/apt/archives/*.deb"} -e ssh root@piac_wireless:/ /home/mrx/Docs/RPi/backup/piac/piac_usb-root that was not executing the exclusions correctly. – dentex Apr 7 '18 at 10:53
  • @dentex I'd strongly recommend against using eval for this -- there's too many ways for it to go wrong. An array would be a better way to do this. See here, here, and here for examples. – Gordon Davisson Apr 7 '18 at 19:10
  • Thanks for the suggestion. I'll try to implement the solution with the array, in order to pass the exclusion list to rsync. – dentex Apr 10 '18 at 19:37

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.

  • If you do use eval, you really need to use double-quotes (eval "$i") to avoid extra-extra-weird effects. For example, try this with a="cat -nT index.php |grep ' .* '" (i.e. the regex should match two spaces with any sequence of characters between them) and see what actually happens. For extra credit, explain why it happens. – Gordon Davisson Nov 24 '13 at 20:59
  • @GordonDavisson double quotes added. – jlliagre Nov 24 '13 at 21:16

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

Try this:

i='echo hi'; $i
  • 1
    That's correct, but it's not an answer to the OP's question, really. – slhck Nov 24 '13 at 9:29
  • its for me REALY :P – nwgat Jan 13 '15 at 20:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.