开发者

Quoting in a bash variable assignment

开发者 https://www.devze.com 2023-01-15 20:13 出处:网络
Using the following simplified code extract: DIR=\'a b\' mount_command=\"./mount.cpfs $loop $DIR -f $OPTS\"

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开发者_JAVA技巧 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
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号