转义bash v3 heredoc中的近括号字符

时间:2015-08-19 12:37:04

标签: bash escaping heredoc

我需要在heredoc中嵌入shell脚本的片段,作为创建cloud-init脚本的一部分,以配置Ubuntu 14.04 LTE机器。演示该问题的脚本的简化版本如下:

#!/bin/bash

cloudconfig=$(cat <<EOF
    if host \$NAMESERVER 1>/dev/null 2>&1; then
    case \$reason in
    BOUND|RENEW|REBIND|REBOOT) nsupdate -k /var/lib/dhcp/nsupdate.key << EOX
    server \$NAMESERVER
    update delete \$HOST_NAME A
    update add \$HOST_NAME \$TTL A \$HOST_ADDR
    send
    EOX
    ;;
    esac
    fi
EOF
)

echo "${cloudconfig}"

运行上述脚本失败如下:

Little-Net:orchestration minfrin$ bash /tmp/test.sh
could not read key from /var/lib/dhcp/nsupdate.{private,key}: file not found
couldn't get address for '$NAMESERVER': not found

有问题的角色是“REBOOT”右侧的右括号,显而易见的解决办法是逃避角色:

BOUND|RENEW|REBIND|REBOOT\) nsupdate -k /var/lib/dhcp/nsupdate.key << EOX

然后,这个反斜杠字符最终会出现在最终的cloudconfig变量中,而该变量又会破坏输出:

Little-Net:orchestration minfrin$ bash /tmp/test.sh
    if host $NAMESERVER 1>/dev/null 2>&1; then
    case $reason in
    BOUND|RENEW|REBIND|REBOOT\) nsupdate -k /var/lib/dhcp/nsupdate.key << EOX
    server $NAMESERVER
    update delete $HOST_NAME A
    update add $HOST_NAME $TTL A $HOST_ADDR
    send
    EOX
    ;;
    esac
    fi

上面的这个特定片段是正在编写的依赖于变量插值的较大文件的一部分,因此在那里引用带有&gt;&gt;“EOF”的heredoc会破坏我们脚本的其余部分。

如果没有通过heredoc泄漏转义字符,我如何逃避“)”字符?

3 个答案:

答案 0 :(得分:2)

由于这似乎是一个bash 3.x解析问题(因为它可以在bash 4.x中工作,因为可以看到here),你需要避免解析器混淆。这似乎对我有用:

#!/bin/bash

rp=")"
cloudconfig=$(cat <<EOF
    if host \$NAMESERVER 1>/dev/null 2>&1; then
    case \$reason in
    BOUND|RENEW|REBIND|REBOOT${rp} nsupdate -k /var/lib/dhcp/nsupdate.key << EOX
    server \$NAMESERVER
    update delete \$HOST_NAME A
    update add \$HOST_NAME \$TTL A \$HOST_ADDR
    send
    EOX
    ;;
    esac
    fi
EOF
)

echo "${cloudconfig}"

答案 1 :(得分:1)

由于你没有任何参数,你实际上想在这里的文档中扩展,我只会引用整个事情(这可以为你节省大量的显式反斜杠):

#!/bin/bash

cloudconfig=$(cat <<'EOF'
    if host $NAMESERVER 1>/dev/null 2>&1; then
    case $reason in
    BOUND|RENEW|REBIND|REBOOT) nsupdate -k /var/lib/dhcp/nsupdate.key << EOX
    server $NAMESERVER
    update delete $HOST_NAME A
    update add $HOST_NAME $TTL A $HOST_ADDR
    send
EOX
    ;;
    esac
    fi
EOF
)

echo "${cloudconfig}"

请注意,您也不能缩进EOX,否则当您使用它时,此处的文档将无法正确终止。

但是,更容易的是,根本不使用这里的文件;只需在参数赋值中使用嵌入的换行符。

#!/bin/bash

cloudconfig='
if host $NAMESERVER 1>/dev/null 2>&1; then
  case $reason in
    BOUND|RENEW|REBIND|REBOOT) nsupdate -k /var/lib/dhcp/nsupdate.key << EOX
    server $NAMESERVER
    update delete $HOST_NAME A
    update add $HOST_NAME $TTL A $HOST_ADDR
    send
EOX
    ;;
  esac
fi'

echo "${cloudconfig}"

如果您需要允许某些参数扩展,您仍然可以使用多行字符串进行一些修改。无论何时需要插值,请关闭单引号并立即打开双引号。扩展完成后,关闭双重并重新打开单个。

cloudconfig='
    if [[ $SOMEVARIABLE =='"$value"' ]]; then
    ...
'

答案 2 :(得分:0)

我通过在HEREDOC内手动转义括号来解决此问题,然后在其后加上sed语句以删除反斜杠。

long_string=$(cat << 'HEREDOC'
This is a string with \( escaped \) parenthesis.
HEREDOC
)

long_string=$(echo $long_string | sed -e 's:\\(:(:g' -e 's:\\):):g')

使用bash 4+会更好,但是随后我必须让团队中的所有人都使用mac's才能获得bash 4 +。