【问题标题】:Quoting in a bash variable assignment引用 bash 变量赋值
【发布时间】:2010-09-08 07:35:47
【问题描述】:

使用以下简化代码提取:

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

当跟踪打开时执行这一行:

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

但是DIR 没有被引用,所以ab 作为不同的参数传递,而不是相同的参数传递给./mount.cpfs

创建这样的命令序列,然后将其扩展为以后的命令行的最佳方法是什么?

请记住,代码示例被简化为核心问题,我在几个地方使用mount_command,并在其前后添加了各种内容,DIR 是用户传入的。在分配给mount_command 时,我尝试了几种引用DIR 的组合,以及使用数组的原始尝试。

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

更新0

请测试您的建议,我认为解决方案并不简单。

【问题讨论】:

标签: linux bash shell ubuntu


【解决方案1】:

bash 中最好的方法是使用数组。该数组将保持分开的单词分开,并在单个单词中保留空格:

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

"${mount_command[@]}" 展开时,每个元素都作为sudo 的单个参数展开,即使它有空格。

请注意我是如何引用"$DIR" 而不是$OPTS,因为您的$OPTS 包含多个要作为单独的单词传递给mount 命令的单词,但$DIR 应该放在一起。但是,您可以使用与 mount_command 相同的方式将 OPTS 设为数组,并将其在 mount_command 定义中扩展为 "${OPTS[@]}"

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

在 bash 脚本中构建了大量命令后,我发现数组远优于试图找出正确的引用(如果可能的话)以将命令维护在一个简单的字符串中。

【讨论】:

    【解决方案2】:

    尝试使用 eval

    给定以下脚本,称为 test

    #!/bin/bash
    # test
    
    echo $1
    

    如果我这样做:

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

    输出

    a b
    

    this question

    【讨论】:

    • 这可行,但不如 camh 的答案那么棒。它还需要使用mount_commandeval 放在命令上,这有点侵入性。有没有办法在最终命令上不使用 eval 来做到这一点?
    【解决方案3】:
    DIR='a b'
    OPTS='-o allow_other,attr_timeout=0'
    mount_command="./mount.cpfs '$loop' '$DIR' -f '$OPTS'"
    eval sudo $mount_command
    

    这应该可以。很难理解 shell 中的引用,因为它太多了 不同于其他语言。

    在您的情况下,您需要引用 '$DIR' 变量(最好是所有可以包含空格的变量)。这很清楚。 但是 shell 只解释一次字符串!你需要指示它 使用命令 eval 重新解释。如果没有 eval,变量 $mount_command 是 展开但不处理内部引用。

    您可以阅读我的简短文章: White Shell

    【讨论】:

      【解决方案4】:

      试试

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

      试试这个:

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

      【讨论】:

      • 不,这不起作用:它会生成:+ sudo ./mount.cpfs /dev/loop0 '"a' 'b"' -f -o default_permissions,allow_other,attr_timeout=0
      • 一个不错的建议,但我并不是在寻找像这样强烈的黑客攻击 :)
      • 现在它会生成+ sudo ./mount.cpfs /dev/loop0 'a\' 'b' -f -o default_permissions,allow_other,attr_timeout=0 -- 分词扩展变量时不会解析引用或转义,因此您无法在变量本身中执行任何操作来防止“a b”被解析为多个词。我将重复丹尼斯威廉姆森的建议阅读BashFAQ/050
      猜你喜欢
      • 2010-12-29
      • 1970-01-01
      • 1970-01-01
      • 2012-04-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多