Matt Joiner
Matt Joiner

Reputation: 118680

Quoting in a bash variable assignment

Using the following simplified code extract:

DIR='a b'
mount_command="./mount.cpfs $loop $DIR -f $OPTS"
sudo $mount_command

Executes this line when trace is on:

+ sudo ./mount.cpfs /dev/loop0 a b -f -o default_permissions,allow_other,attr_timeout=0

But DIR is not quoted, and so a and b are passed as different parameters, rather than the same to ./mount.cpfs.

What's the best way to go about creating a command sequence like this, and then expanding it into a later command line?

Please keep in mind the code example is simplified to the core problem, I'm using mount_command in several places with various additions before and after it, and DIR is passed in by the user. I've tried several combinations of quoting DIR when assigning to mount_command, and a primitive attempt at using an array.

Example Usage of mount_command

mount_command="./mount.cpfs $loop $DIR -f $OPTS"
case "$MODE" in
    gdb)
        sudo gdb -return-child-result -x gdbbatch \
            --args $mount_command
        ;;
    normal)
        sudo $mount_command
        ;;
    valgrind)
        sudo valgrind --track-fds=yes --leak-check=full --malloc-fill=0x80 \
            --free-fill=0xff $mount_command
        ;;
    *)
        echo "Mode '$MODE' unknown"
        mounted=''
        exit 2
        ;;
esac

Update0

Please test your suggestions, I don't think the solution is straightforward.

Upvotes: 2

Views: 596

Answers (4)

Tomek Wyderka
Tomek Wyderka

Reputation: 1475

DIR='a b'
OPTS='-o allow_other,attr_timeout=0'
mount_command="./mount.cpfs '$loop' '$DIR' -f '$OPTS'"
eval sudo $mount_command

This should work. It's hard to understand quoting in shell because it's much different than in other languages.

In you case you need to quote '$DIR' variable (and better all which can contains spaces). This is clear. But shell interpret strings just one time! You need to instruct it to reinterpret by using command eval. Without eval, variable $mount_command is expanded but quoting inside is not processed.

You can read my short article about this: White Shell

Upvotes: 0

camh
camh

Reputation: 42538

The best way in bash is to use an array. The array will keep words separated that are meant to be separated and keep spaces inside individual words:

DIR='a b'
mount_command=(./mount.cpfs $loop "$DIR" -f $OPTS)
sudo "${mount_command[@]}"

When "${mount_command[@]}" is expanded, each element is expanded as a single argument to sudo even if it has spaces.

Note how I quoted "$DIR" but not $OPTS, as your $OPTS contains multiple words intended to be passed to the mount command as separate words but $DIR should be kept together. However, you could make OPTS an array in the same way as mount_command and expand it inside the mount_command definition as "${OPTS[@]}":

DIR='a b'
OPTS=(-o default_permissions,allow_other,attr_timeout=0)
mount_command=(./mount.cpfs $loop "$DIR" -f "${OPTS[@]}")
sudo "${mount_command[@])"

Having done lots of building of commands in bash scripts, I find arrays to be far superior to trying to figure out the correct quoting (if possible) to maintain the command in a simple string.

Upvotes: 5

Joel
Joel

Reputation: 30166

Try using eval.

Given the following script, called test:

#!/bin/bash
# test

echo $1

If I do this:

DIR='a b'
CMD=."/test \"$DIR\""
eval $CMD

It outputs

a b

See this question

Upvotes: 1

aularon
aularon

Reputation: 11110

Try

mount_command="./mount.cpfs $loop \"$DIR\" -f $OPTS"

Try this instead:

DIR='a b'
#following line replaces space occurrences with "\ "
DIR=`echo $DIR | replace ' ' '\ '`
mount_command="./mount.cpfs $loop $DIR -f $OPTS"
sudo $mount_command

Upvotes: -1

Related Questions