8

The following works in my shell (zsh):

> FOO='ls'
> $FOO
file1 file2

but the following doesn't:

> FOO='emacs -nw'
> $FOO
zsh: command not found: emacs -nw

even though invoking emacs -nw directly opens Emacs perfectly well.

Why?

  • 1
    Are the backticks in your 2nd command just OCR errors or do you really used them in your script? – try-catch-finally Dec 28 '14 at 20:53
  • @Stéphane Chazelas, I think "fixing" possible non-mistakes in the OP (after answering) tends to be very misleading... – try-catch-finally Dec 28 '14 at 20:54
  • 1
    Just as a remark: In bash that would work. – Hauke Laging Dec 28 '14 at 20:54
  • 1
    @HaukeLaging, that would work unless IFS was modified. – Stéphane Chazelas Dec 28 '14 at 20:55
  • 3
    @try-catch-finally But in this case the error message shows that he used quotes, not backticks. – Hauke Laging Dec 28 '14 at 20:55
  • @StéphaneChazelas But it's not an IFS issue in zsh? – Hauke Laging Dec 28 '14 at 20:56
  • -- All, sorry for the backtick typo in the OP. I did use single quotes (as the OP currently shows after @Stephane's edit) – Amelio Vazquez-Reina Dec 28 '14 at 20:57
  • 3
    @HaukeLaging, no zsh behaves as you'd expect. When you type $cmd, it runs the command whose name is stored in $cmd. bash (and most other Bourne like shells) invoke the split+glob operator on unquoted variables, zsh fixed that. – Stéphane Chazelas Dec 28 '14 at 20:59
  • @StéphaneChazelas :-) I guess what "you" expect depends pretty much on whom you ask. – Hauke Laging Dec 28 '14 at 21:01
  • 1
    @HaukeLaging, after seeing the number of variables left unquoted in scripts written for other Bourne like shells despite the consequences, I tend to believe most people expect the zsh way. – Stéphane Chazelas Dec 28 '14 at 21:03
  • 1
    @StéphaneChazelas - that is a good point. Unfortunately zsh's behavior is also inconsistent. It splits $(cmd subs) but not ${vars}. My preference is for ksh93's $IFS handling - where the handling is consistent, and whitespace can either delimit per byte or sequence depending on how $IFS is assigned. – mikeserv Dec 28 '14 at 23:26
  • 1
    @mikerserv, doing split+glob upon parameter expansion doesn't make sense when you have array variables, however doing split (not glob) does make sense upon command substitution as that's often what you want (though not often on anything but newline), I don't see an inconsistency in that. That behaviour of ksh you're referring to is partly available in zsh (doubling whitespace in IFS prevents the special handling), the other way round is not a problem as that's just empty removal). – Stéphane Chazelas Dec 29 '14 at 16:01

2 Answers2

9

Because there's no command called emacs -nw. There's a command called emacs to which you can pass a -nw option.

To store commands, you generally use functions:

foo() emacs -nw "$@"
foo ...

To store several arguments, you generally use arrays:

foo=(emacs -nw)
$foo ...

To store a string containing several words separated by spaces and have it split on spaces, you can do:

foo='emacs -nw'
${(s: :)foo} ...

You could rely on word splitting performed on IFS (IFS contains space, tab, newline and nul by default):

foo='emacs -nw'
$=foo ...
  • Have you noticed the backticks in the 2nd command in the OP? – try-catch-finally Dec 28 '14 at 20:52
  • Thanks @Stephane, this is very helpful, as usual. I am now hoping to make sense of this answer in the context of this other question. To do so, I tried >my_wrapper.sh "some text" (date '+%d') where the my_wrapper is supposed to invoke the command date '+%d', but it doesn't work. – Amelio Vazquez-Reina Dec 28 '14 at 21:11
  • @user815423426 - have a look at Stéphane's answer here maybe. You might also glance at my own. The problem you're having is due to the shell's order of evaluations while parsing your command and the (subshell) - the parser needs to read those at the same time it recognizes a variable expansion - which obviously cannot happen if the (parens) are within the expansion. You need eval - or and alias - in some form or another. – mikeserv Dec 28 '14 at 23:33
  • Nevermind the problem with date is due to the fact that crontabrequires escaping %d as @Gilles mentioned here – Amelio Vazquez-Reina Dec 29 '14 at 14:03
5

For future readers it should be mentioned that the "standard" way of doing such thing is to evaluate the arguments stored in variable:

eval "$foo"

One can also evaluate expression with command substitution:

$(expr "$foo")

echoed variable (or otherwise printed) works as well:

$(echo "$foo")

These methods works fine at least in zsh and bash.

jimmij
  • 47,140