如何在bash中编写动态生成的heredoc?

时间:2017-12-14 04:10:54

标签: bash shell ssh heredoc

我有一个连接到远程计算机以执行操作的shell脚本。其中一个操作是设置一个或多个DNS服务器。虽然很容易捕获大量静态数据并通过SSH管道到远程机器:

config_ntp()
{
    ssh -T admin@server_ip <<-NTPSERVER
    sysconf ntp addserver $NTPSERVER
    NTPSERVER
}

创建动态大小的命令列表比我想象的要复杂。我有什么:

DNSSERVERS=(8.8.8.8 8.8.8.7)

config_dns()
{
    cmd=""
    for server in ${DNSSERVERS[@]}; do
        cmd+="network dns add nameserver $server$'\n' "
    done
    cmd+="service dns restart$'\n'"
    echo -e "cmd: $cmd"
    ssh -T admin@server_ip $cmd
}

调用此结果:

$ sh setup.sh
cmd: network dns add nameserver 8.8.8.8$'
' network dns add nameserver 8.8.8.7$'
' service dns restart$'
'
Syntax Error: Invalid character detected: '\'.
Command Result : 22 (Invalid argument)
Exiting...

那是我最新的化身。我按照其他地方的建议玩$'\n' ...之前,我刚刚\n,导致同样的错误。

如何创建一个包含列表(可变长度,动态生成)的命令的变量,以通过ssh管道到远程机器?

3 个答案:

答案 0 :(得分:3)

为什么你想要或需要这里的文件?如果基本上不是一个静态模板,只有一些占位符填充变量,那么通过其他方法为流程提供标准输入几乎肯定更容易。

ssh <<____HERE
commands
more commands "$variable"
____HERE

完全等同于

printf "commands\nmore commands \"%s\"\n" "$variable" | ssh

如果某些部分比仅仅打印某些部分更复杂,那么你可以使它变得非常复杂:

{ complex_function --with --options and arguments  -o stdout
  printf "echo 'anything we send to stdout goes into the pipe'\n"
  if command; then
      while another command; do
          for arguments in $(something to drive a loop); do
               complex stuff
          done
      done
  fi
  : and etc
} | ssh

在管道中使用大括号( commands )而不是大括号{ commands }在子shell中运行commands

您的具体问题是您无法将$'\n'置于常规的双引号字符串中。 echo已经产生了一个换行符,所以你似乎采取了一种非常迂回的方式来构建东西。但试试这个:

# Does this really need to be in a separate variable?
DNSSERVERS=(8.8.8.8 8.8.8.7)
config_dns()
{
    {
      # To be completely correct, notice quoting around array
      for server in "${DNSSERVERS[@]}"; do
        echo "network dns add nameserver $server"
      done
      echo "service dns restart"
    } |
    # maybe add a tee /dev/stderr or something here to see what's going on
    ssh -T admin@server_ip
}

对于美学,我可能会重构

config_dns()
{
    {
        printf 'network dns add nameserver %s\n' "${DNSSERVERS[@]}"
        printf 'service dns restart\n'
    } |
    ssh -T admin@server_ip
}

答案 1 :(得分:1)

首先,您将传递文本传递给命令的stdin并将其作为参数传递。 您应该将ssh调用更改为:

ssh -T admin@server_ip <<< "$cmd"

它使用herestrings将文本传递给命令的stdin(类似于heredocs所做的那样)。

其次,要使$'\n'符号起作用,您需要将其置于双引号之外:

cmd+="network dns add nameserver $server"$'\n'" "

答案 2 :(得分:0)

通过ssh引用非常复杂。如果你不必这样做,就不要这样做。

不要尝试将命令与\n分开,只需使用;即可。它同样有效,并且不需要这么多的引用。

DNSSERVERS=(8.8.8.8 8.8.8.7)

config_dns()
{
    cmd=""
    for server in ${DNSSERVERS[@]}; do
        cmd+="network dns add nameserver $server;"
    done
    cmd+="service dns restart"
    echo -e "cmd: $cmd"
    ssh -T admin@server_ip "$cmd"
}