Skip to content

Bash quoting

I’m always confused by bash’s quoting. I hope to put all my quote wisdom in this post and invoke other’s quote wisdom in the comments. I’ll give some examples of what I mean.

Let’s say you have a file with a space: “bla bla.txt”. If I were to ls that file, I would do:

ls 'bla bla.txt'

This works. However, when I want to do this from a variable (in a script) and do:

command="ls 'bla bla.txt'"
$command

The result is:

ls: cannot access 'bla: No such file or directory
ls: cannot access bla.txt': No such file or directory

You can solve this by using eval:

command="ls 'bla bla.txt'"
eval $command

This gives:

bla bla.txt

Some time ago, I suggested this as answer on somebodies question at userfriendly, to which somebody else said that using eval actually makes things worse:

That’s actually worse. . . as the quoting gets re-parsed (remember, ‘eval’ means “take arguments as shell input”), which means that single quotes in the name break it, horribly, and names with spaces get even _worse_.

Another example: let’s say you have two files:

-rw-r----- 1 halfgaar halfgaar 0 2009-10-18 16:51 bla's bla"s.txt
-rw-r----- 1 halfgaar halfgaar 0 2009-10-18 16:52 normal.txt

I’m gonna run this command on it: find . -mindepth 1 -exec ls ‘{}’ \;. When executed without eval, it says this:

find: missing argument to `-exec' 

With eval, it says:

./normal.txt
./bla's bla"s.txt

Eval seems to be what I need, so what is wrong with using it? Also, shouldn’t that double quote be a problem? If someone can give a situation where that poses problems, I’m all ears.


    1 Comment ( Add comment / trackback )

    1. (permalink)
      Comment by halfgaar
      On January 5, 2011 at 10:23

      Another test case:

      often I do

      EXCLUDE="--exclude /a --exclude /b"
      program $EXCLUDE -- source dest

      I was told again that eval is evil. But what if one of those to-be-excluded dirs contains a space? To test: when I do this:

      touch a b c 'a b c'
      file="-l a b c 'a b c'"
      ls $file

      I get:

      ls: cannot access ‘a: No such file or directory
      ls: cannot access c': No such file or directory
      -rw-r–r– 1 root root 0 2011-01-05 09:17 a
      -rw-r–r– 1 root root 0 2011-01-05 09:17 b
      -rw-r–r– 1 root root 0 2011-01-05 09:17 b
      -rw-r–r– 1 root root 0 2011-01-05 09:17 c

      When I do:

      ls "$file"

      Then:

      ls: invalid option —

      But when I do eval:


      eval ls "$file":

      -rw-r--r-- 1 root root 0 2011-01-05 09:17 a
      -rw-r--r-- 1 root root 0 2011-01-05 09:17 a b c
      -rw-r--r-- 1 root root 0 2011-01-05 09:17 b
      -rw-r--r-- 1 root root 0 2011-01-05 09:17 c

      I could define my excludes without the params and build it with a for loop, but in the end, I still have to include it in the command, so that doesn't make the problem go away. So, how do I do this?

    Post a comment

    (required)
    (required)

    Your email is never published nor shared.

    (optional)
    Allowed HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>