简体   繁体   中英

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.

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.

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM