12

I'm trying to pass 2 arguments to a command and each argument contains spaces, I've tried escaping the spaces in the args, I've tried wrapping in single quotes, I've tried escaping \" but nothing will work.

Here's a simple example.

#!/bin/bash -xv

ARG="/tmp/a b/1.txt"
ARG2="/tmp/a b/2.txt"

ARG_BOTH="\"$ARG\" \"$ARG2\""
cat $ARG_BOTH

I'm getting the following when it runs:

ARG_BOTH="$ARG $ARG2"
+ ARG_BOTH='/tmp/a\ b/1.txt /tmp/a\ b/2.txt'
cat $ARG_BOTH
+ cat '/tmp/a\' b/1.txt '/tmp/a\' b/2.txt
cat: /tmp/a\: No such file or directory
cat: b/1.txt: No such file or directory
cat: /tmp/a\: No such file or directory
cat: b/2.txt: No such file or directory

3 Answers 3

13

See http://mywiki.wooledge.org/BashFAQ/050

TLDR

Put your args in an array and call your program as myutil "${arr[@]}"

#!/bin/bash -xv

file1="file with spaces 1"
file2="file with spaces 2"
echo "foo" > "$file1"
echo "bar" > "$file2"
arr=("$file1" "$file2")
cat "${arr[@]}"

Output

file1="file with spaces 1"
+ file1='file with spaces 1'
file2="file with spaces 2"
+ file2='file with spaces 2'
echo "foo" > "$file1"
+ echo foo
echo "bar" > "$file2"
+ echo bar
arr=("$file1" "$file2")
+ arr=("$file1" "$file2")
cat "${arr[@]}"
+ cat 'file with spaces 1' 'file with spaces 2'
foo
bar
Sign up to request clarification or add additional context in comments.

1 Comment

Is there a solution for regular posix shells like dash?
6

This might be a good use-case for the generic "set" command, which sets the top-level shell parameters to a word list. That is, $1, $2, ... and so also $* and $@ get reset.

This gives you some of the advantages of arrays while also staying all-Posix-shell-compatible.

So:

set "arg with spaces" "another thing with spaces"
cat "$@"

Comments

5

The most straightforward revision of your example shell script that will work correctly is:

#! /bin/sh

ARG="/tmp/a b/1.txt"
ARG2="/tmp/a b/2.txt"

cat "$ARG" "$ARG2"

However, if you need to wrap up a whole bunch of arguments in one shell variable, you're up a creek; there is no portable, reliable way to do it. (Arrays are Bash-specific; the only portable options are set and eval, both of which are asking for grief.) I would consider a need for this as an indication that it was time to rewrite in a more powerful scripting language, e.g. Perl or Python.

2 Comments

Would you care to say why set could be a source of grief?
There's only one "$@", so it can't be used for more than one thing at a time. And set -- $VARIABLE; cmd "$@" does word splitting exactly the same way cmd $VARIABLE does, so that's no good. And you have to make sure you aren't getting one of the many other things set does by accident.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.