【问题标题】:How do I pass subshell results (array) to an SSH command?如何将 subshel​​l 结果(数组)传递给 SSH 命令?
【发布时间】:2017-11-25 21:50:50
【问题描述】:

试试这个方法:

#!/bin/bash
myvals=`psql -d mydb -c "select id from table1 where 't'"`

ssh user1@host1.domain.tld "for i in $myvals; do echo \$i >> values; done"

只要 psql 只返回一个值,它就可以正常工作。但如果它有几个值,我会收到以下回复:

bash: -c: line 1: syntax error near unexpected token `2'
bash: -c: line 1: `2'

另外,我尝试过:

myvals='1 2 3'

然后它就可以正常工作了:值 1 2 3 被附加到远程主机上的“值”文件中;没有错误信息。 如果我尝试另一个子 shell 命令,例如 myvals=ls /bin,错误会再次出现。 很明显 $myvals 已经在本地主机上进行了评估,但是是什么让 subshel​​l 结果如此不同?

【问题讨论】:

  • 是什么让 subshel​​l 结果如此不同? 答:变量中有换行符会搞砸解析器。试试myvals=$'1\n2\n3'
  • myvals 在此脚本中根本不是数组。运行declare -p myvals 转储它的类型和值——如果输出declare -- myvals=... 它是一个字符串;如果它发出 declare -a myvars=... 它是一个数组。

标签: bash ssh psql subshell


【解决方案1】:

如果它不是真正的数组...

像数组一样迭代字符串本身就是错误的。 Don't do it. 也就是说,要生成您的值的安全转义 (eval-safe) 版本,请使用 printf %q

#!/bin/bash

myvals=`psql -d mydb -c "select id from table1 where 't'"`
printf -v myvals_q %q "$myvals"

ssh user1@host1.domain.tld \
  "myvals=$myvals_q;"' for i in $myvals; do echo "$i"; done >>values'

如果你真的有一个数组

#!/bin/bash
readarray -t myvals < <(psql -d mydb -c "select id from table1 where 't'")
printf -v myvals_q '%q ' "${myvals[@]}"

ssh user1@host1.domain.tld \
  "myvals=( $myvals_q );"' for i in "${myvals[@]}"; do echo "$i"; done >>values'

如果您首先不需要在本地存储值

#!/bin/bash

ssh user1@host1.domain.tld \
  'while read -r i; do echo "$i"; done >>values' \
  < <(psql -d mydb -c "select id from table1 where 't'")

一般说明

  • 在循环中一遍又一遍地运行echo "$i" &gt;&gt;values 效率低下:每次运行该行时,它重新打开values 文件。相反,在整个循环中运行重定向&gt;values ;这会在循环开始时将文件截断一次,并附加其中生成的所有值。
  • 不带引号的扩展通常是危险的。例如,如果foo='*',则$foo 将替换为当前目录中的文件列表,但"$foo" 将发出确切的内容——*。同样,制表符、空白运行和各种其他内容可能会因未加引号的扩展而意外损坏,即使直接传递给 echo
  • 您可以在同一字符串中切换引用类型——因此,"$foo"'$foo' 是一个字符串,其中的第一部分替换为名为 foo 的变量的值,第二部分是 准确字符串$foo

【讨论】:

  • 我猜 OP 想要遍历命令 psql... 的行...所以也许像 ssh user1@host1.domain.tld 'while read line; do echo "$line"; done' &lt; &lt;(psql ...) 这样的东西会更合适。
  • shrug -- 这是一个判断,这里给出的答案(尤其是readarray 变体)确实做了我们都同意的事情' 要求(迭代远程端的值)。以这种方式在带外传递内容当然有它的位置,但这意味着你不能将标准输入用于其他任何事情(包括传递脚本代码本身)。
  • @gniourf_gniourf, ...进一步考虑,添加该选项非常值得编辑;这样做。
  • 非常有用的信息,感谢 Charles Duffy。案例 3(没有在本地存储值)工作正常。但是在情况 2 中我收到此错误: ./fence2.sh: line 6: unexpected EOF while looking for matching `'' ./fence2.sh: line 7: syntax error: unexpected end of file 否则,情况 2 会更好b/c 我需要将命令发送到多个主机。
  • @Charles,你的意思可能是在结尾加上单引号,如案例 1:done >>values' 这样就可以了。
【解决方案2】:

您可以将输出作为文件发送:

#!/bin/bash
psql -d mydb -c "select id from table1 where 't'" > /tmp/values
scp values user1@host1.domain.tld:/tmp/

或将其通过管道传送到远程主机:

psql -d mydb -c "select id from table1 where 't'" | \
  ssh user1@host1.domain.tld 'while read line; do echo $line; done'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-03
    • 2018-04-14
    • 2022-11-10
    • 1970-01-01
    • 1970-01-01
    • 2012-02-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多