Spaces in Linux environmental variables?

12,674

Solution 1

The problem is not that your environmental variable contains a space. You're forgetting to quote your arguments:

cd "$Foo"

If you don't quote $Foo, the cd command will actually see two (or more) arguments, since whitespace by default separates arguments. In your specific case, you'd pass /media/foo/can and haz/bar separately, since the shell splits them at the space. Double-quoting variables is a good habit to develop, and will save you in quite a lot of cases where whitespace is involved.

If you want a really ugly way of achieving this without quotes, overwrite your cd builtin like so:

cd() { builtin cd "$*"; }

This will work if the path contains spaces, since $* will contain the individual arguments (/media/foo/can and haz/bar), separated by the first IFS character (usually a space), and then you'll glue them together with the double quotes.

It is never a good idea to overwrite builtins like this, though.

Solution 2

As @slhck said, the correct way to do this is to put double-quotes around the variable reference (e.g. cd "$Foo"). The reason for this is the order that the shell parses various aspects of the command line: first it scans for quotes and escapes (and removes them as it applies their effects), then it replaces variable references with their values (e.g. $Foo -> /media/foo/can haz/bar), then it goes back through the replaced values and applies wildcard expansion (*.txt -> a.txt b.txt etc) and word splitting (/media/foo/can haz/bar -> "/media/foo/can" "haz/bar"), but it never goes back and looks for quotes and/or escapes in the variable values. As a result, embedding quotes and/or escapes in the value of a variable does nothing even slightly useful; by the time they're part of the command line, it's too late for them to have the intended effect. I only know of three ways to avoid word splitting on the expanded value:

  1. Double-quote the variable reference. It's not as convenient, but it's a requirement of standard shell syntax.
  2. Change the value of IFS (the list of characters that're used for word splitting on expanded variables). Then watch as all the scripts that depend on the standard word splitting behavior break weirdly. Please don't do this. Seriously.
  3. Switch to a shell that doesn't do word splitting on expanded variables: zsh. You'll have to get used to its other differences from bash, but if you really want this, it's the best way to go. I don't use it myself, but those who do seem to like it a lot...
Share:
12,674

Related videos on Youtube

A T
Author by

A T

Updated on September 18, 2022

Comments

  • A T
    A T almost 2 years

    Given directory: /media/foo/can haz/bar, I have tried editing bash.rc with:

    export Foo=/media/foo/can haz/bar
    export Foo=/media/foo/can\ haz/bar
    export Foo='/media/foo/can haz/bar'
    export Foo="/media/foo/can haz/bar"
    export Foo='"/media/foo/can haz/bar"'
    export Foo="'/media/foo/can haz/bar'"
    

    Each time though, I couldn't get something like the following to work (without using "$Foo"):

    cd $Foo
    

    How do I set an environmental variable with spaces on a Linux system?

    • A T
      A T almost 11 years
      Maybe if I set an alias or something?
  • A T
    A T almost 11 years
    As mentioned, I want to avoid doing that; that is the whole problem I am trying to solve.
  • slhck
    slhck almost 11 years
    Did you sneak that part in? I don't remember it being there in the first revision of the question I saw :) Anyhow, your question is, "How do I set an environmental variable with a space?", and the answer is, you can just do it. How you handle it is a different problem, and that you couldn't get it to work without quotes is just a matter of how the shell works.
  • slhck
    slhck almost 11 years
    Zsh also nicely auto-completes cd $Foo to cd /path\ with/spaces. I've found Bash to do this wrongly if you trigger it with Ctrl-A-E, but I haven't used Bash in a while, so it might be possible to configure this as well.
  • slhck
    slhck almost 11 years
    @AT Have you seen my update?