【问题标题】:Bash: expand variables, but not special charactersBash:扩展变量,但不扩展特殊字符
【发布时间】:2015-02-21 00:52:33
【问题描述】:

我有一个bash 脚本,它通过将包含expect 代码片段的数十个不同文件拼接在一起来创建和执行expect 脚本。这些文件包含需要扩展的环境变量。示例:

expect.piece:

send "command\r"
sleep $timeout
send "command argument\r"

script.sh:

#let's try it like this
eval echo $(cat expect.piece)
#or maybe like this
eval "echo \"$(cat expect.piece)\""

输出:

send command\r sleep 1 send command argument\r
send commandr
sleep 1
send command argumentr

期望的输出:

send "command\r"
sleep 1
send "command argument\r"

我需要一个没有sed 字符串替换(有很多环境变量)并且不修改原始expect 脚本文件的解决方案。我想它可以逐行完成,但有没有更优雅的解决方案?

【问题讨论】:

  • 你想执行你想要的输出还是只是想在你的 bash 脚本中打印它?因为 eval 将尝试执行 echo 命令的输出,即它将尝试在 bash 脚本中执行 send,这是一个期望的命令而不是 bash 的
  • 我需要创建一个稍后将使用expect 执行的脚本。
  • bash 对这种模板扩展没有很好的支持。相反,请将变量保留在 expect.piece 中,并确保在适当的环境中执行它,例如 timeout 的值为 1。
  • 好的,但是让我们重新考虑 sed 然后(仍然不是题外话),如果我有一个像这样的巨大替换队列:sed "s/\$timeout/$timeout/g;s/\$var/$var/g"(有效,但有很多变量)可以将其简化为将 \$whatever 替换为 $whatever 的值?

标签: bash variables eval expect expand


【解决方案1】:

bash 中的默认字段分隔符是 space,因此在执行 eval echo $(cat expect.piece) 之前将输入文件分隔符设置为新行,例如 IFS=$(echo -e '\n')

最终脚本是:

#Storing original field separator in variable OFS
OFS=$IFS
#Setting IFS as new line using echo -e. Unfortunately IFS="\n" does not work in bash
IFS=$(echo -e '\n')
eval echo $(cat expect.piece)
#Resetting the field separator as space
IFS=$OFS

【讨论】:

  • 我已经尝试过,就像许多其他实验一样,它产生了:-sh: send: command not found
  • 因为send 是一个expect 命令,你不能在bash 中使用它,你需要使用expect 的解释器来运行它。
【解决方案2】:

还有另一种方法可以将带有-c 标志的expect 代码放入shell 脚本中,如下所示。

script.sh

#Calling the expect.piece file code here
expect -c expect.piece

您可以将可选的命令行值用作 ,

expect -c "set count 1" myscript.exp

变量count将在expect脚本文件myscript.exp中使用。

你可以直接把整个代码写成

expect -c "
             send \"command\r\"
             sleep $timeout
             send \"command argument\r\"
"

注意在需要的地方使用反斜杠来转义双引号。也可以使用单引号。但是,如果使用双引号,则只能发生 shell 替换。

请参考here 了解更多关于expect-c 标志的信息。

【讨论】:

    【解决方案3】:

    从您的问题中不清楚您想要此输出的上下文。如果可以将 Expect 脚本嵌入到 here 文档中,那么您想要的就是微不足道的。

    #!/bin/sh
    
    timeout=1
    
    cat <<____HERE
    send "command\r"
    sleep $timeout
    send "command argument\r"
    ____HERE
    

    (也许您甚至可以将cat 替换为expect,但我对Expect 不够熟悉,无法提出任何建议。)

    如果您需要从文件中获取输入,并且只需要扩展一组有限的变量,您可以使用 sed 来实现。

    sed "s/\$timeout/$timeout/g" file
    

    如果您需要更通用的解决方案,您可能需要切换到 Perl:

    perl -pe 's/\$(\w+)/$ENV{$1} || "\$$1" /ge' file
    

    但这需要您export(或以其他方式向 Perl 公开)您想要导出的 shell 环境变量。 (这不会替换任何未定义的变量;如果您想更改行为的那方面,请更改 || 之后的值。)

    【讨论】:

      【解决方案4】:

      我为这个问题发明了解决方案,这是一个杂牌,但它有效。

      expect.piece:

      sleep $timeout
      send "foo bar\r"
      send "$(date)\r"
      

      script.sh:

      timeout=1
      eval echo $(sed " \
      s/\\\/\\\\\\\/g; \
      s/\"/\\\\\"/g; \
      s/\"/\\\\\`/\"/\\\\\`/g; \
      " "expect.piece" | tr '\n' '+') | tr '+' '\n'
      

      输出:

      sleep 1
      send "foo bar\r"
      send "Wed Feb 18 03:19:24  2015\r"
      

      首先,我们需要转义文件中的所有反斜杠、反引号和引号,因为它们将在评估期间被删除。然后,我们需要用加号替换所有换行符,以使其成为一行。之后,我们评估该行(评估将“扩展”所有环境变量和命令替换)并将加号替换回换行符。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-17
        • 2012-03-17
        • 1970-01-01
        • 1970-01-01
        • 2017-10-11
        • 2015-12-13
        相关资源
        最近更新 更多