Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I'm trying to escape the space char for a path in Bash, but neither using a backslash or quotes works.

.sh script:

ROOT="/home/hogar/Documents/files/"
FILE=${ROOT}"bdd.encrypted"
DESTINATION="/home/hogar/Ubuntu\ One/folder"

mv ${FILE} ${DESTINATION}

After execute the script (./file) this is the result:

mv: target ‘One/folder’ is not a directory

Why does the mv command split the string and who do I stop this?

share|improve this question
6  
Recommended reading: $VAR vs ${VAR} and to quote or not to quote, Why do I need to quote variable for if, but not for echo?, When is double-quoting necessary? if you want to know everything you didn't need to know, and other quoting questions. –  Gilles Jan 10 at 2:42

3 Answers 3

up vote 14 down vote accepted

You are expanding the DESTINATION variable, if you did echo this is what you would get:

echo ${DESTINATION}
/home/hogar/Ubuntu\ One/folder

But mv doesn't understand this:

mv ${FILE} ${DESTINATION}                                                
mv: cannot move ‘/home/hogar/Documents/files/bdd.encrypted’ to ‘/home/hogar/Ubuntu\\ One/folder’: No such file or directory

(for some reason my mv is more verbose)

To prevent this you should use quotes instead:

mv "${FILE}" "${DESTINATION}"

If you don't need expansion (since you are already expanding before) just using "$..." should suffice:

mv "$FILE" "$DESTINATION"
share|improve this answer
1  
Furthermore, if you double-quote everything consistently, you don't need the backslash before the space. –  200_success Jan 10 at 5:12

Yes you escaped space when you assign the value to $DESTINATION,

But when you use it with mv command, you didn't

mv ${FILE} ${DESTINATION}

Use this instead:

mv "${FILE}" "${DESTINATION}"
share|improve this answer

Your step to prepare the variables is "close enough" and will work, but it does show a weakness in understanding (which is why you posted!)

The assignment to DESTINATION needs to be only one of these two:

DESTINATION="/home/hogar/Ubuntu One/folder"

or

DESTINATION=/home/hogar/Ubuntu\ One/folder

The double-quotes turns on quoting (a form which has some magic) and the backslash is capable of quoting a single character following it. Personally I think the form using double-quotes is preferable because visually it's nicer.

By the way, for similar reasons I would do the FILE assignment like:

FILE="${ROOT}bdd.encrypted"

which shows the fairly rare occasion to use ${NAME} instead of simply $NAME - to separate the variable name from any letter following. In fact, if you didn't have a trailing / on the definition of ROOT, I'd be writing

FILE="$ROOT/bdd.encrypted"

which looks even better. It will also give you occasional benefits that I won't go into; let's just say in my experience, trailing slashes tend to be more unwelcome than welcome, and they are definitely not needed. (It does have consequences when it comes to symlinks but that's definitely too much detail on this already large answer).

The crux of your issue is actually taking place at the mv command, which should be written as

mv "$FILE" "$DESTINATION"

because these days, you never know when a variable representing a path will have spaces in it. Once again, note the avoidance of braces for a simple variable expansion.

The reason you got a problem is because of the process the shell uses to construct command lines. If you read the shell manual carefully, it basically says that variables (and a few other things) get expanded THEN it goes looking for spaces. Of course there's some more complexity in the process, but this is the gist of the problem for you.

One further point that's related and well worth knowing about: the distinction between $*, $@, "$*" and "$@". So I'll talk about it!

If you have a shell script that may be called as:

foo program "attached is the software" "please read the accompanying manual"

then foo will see the two strings as $2 and $3 (which you should access as "$2" and "$3" for obvious reasons). If, however, you want to pass this entire set of parameters to another script, the following will suffer horrible splitting on all the spaces:

bar $*

and the following (which was the only way in version 0.X of the very original Bourn shell) will cause all the arguments to be passed as a single string (also WRONG)

bar "$*"

so the following syntax was added as a very special magical case:

bar "$@"

which deliberately quotes the individual members of $* as complete quoted strings but keeps them separate. Everyone is a winner now.

It's kinda entertaining to experiment with other effects of $@ when not used as "$@" but you'll quickly find out it's not actually that entertaining... it is merely a special case that solves a major problem.

All the decent modern shells which support arrays also use @ in a special case:

${arr[*]}
"${arr[*]}"
"${arr[@]}"

where the first one will suffer splitting on all spaces, leading to an unpredictable number of separate words, the second will yield all the array members in one string, and the third will very nicely offer each array member as an individual unbroken quoted string.

Enjoy!

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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