【问题标题】:How to run for loop inside heredoc while accessing remote machine访问远程机器时如何在heredoc中运行for循环
【发布时间】:2019-02-27 03:35:19
【问题描述】:

这是我的脚本,其中我使用heredoc 在远程机器内使用局部变量。但是heredoc 下的循环只取第一个变量值。循环在 heredoc 内运行良好,但值相同。

#!/bin/bash
prod_web=($(cat /tmp/webip.txt));
new_prod_app_private_ip=($(cat /tmp/ip.txt));
no_n=($(cat /tmp/serial.txt));
ssh -t -o StrictHostKeyChecking=no ubuntu@${prod_web[0]} -p 2345 -v << EOF
set -xv
for (( x = 0; x < '${#no_n[@]}'; x++ ))
do
sudo su
echo '${no_n[x]}'
echo '${new_prod_app_private_ip[x]}'
curl -fIkSs https://'${new_prod_app_private_ip[x]}':9002 | head -n 1 
done
EOF

所以,我的 ip.txt 文件包含如下值:

10.0.1.0
10.0.2.0
10.0.3.0

我的 serial.txt 文件:

9
10
11

所以,我的循环只针对远程机器中的第一个 IP(存在于 /tmp/ip.txt)运行了 3 次。我想为所有三个 IP 运行它。我的远程 ip 存在于文件 /tmp/webip.txt

卡了很长时间,感谢任何帮助。我还有其他解决方案吗?

【问题讨论】:

  • 这里的 doc 不运行任何命令,除了命令替换和变量扩展。就像echo heredoc。您希望循环在本地机器还是远程执行?
  • @KamilCuk 我希望在远程机器上执行循环。
  • 要调试此问题,请将ssh ... 替换为cat 并查看远程主机看到的内容。
  • @thatotherguy 我已经设置了-xv,还需要吗?
  • 没有人需要任何东西,但是如果您查看您要求主机运行的脚本,就会立即明白它为什么会这样。

标签: bash loops variables eof heredoc


【解决方案1】:

有 2 个环境。在您的本地机器和远程机器上。您需要考虑如何在这些机器之间传输数据/变量/状态/对象/句柄。 如果您在本地机器上设置了一些东西(即prod_web=($(cat /tmp/webip.txt));),然后只是 ssh 到远程主机(即ssh user@host 'echo "${prod_web[@]}"'),则该变量将不可见/导出到远程机器。你可以:

  • scp 文件 {ip,serial}.txt 并在远程机器上执行整个脚本,然后清理,即。从远程机器上删除 {ip,serial}.txt 文件
  • 将文件 {ip,serial}.txt 以某种方式合并/加入/粘贴到 ssh 的标准输入,然后在删除机器上读取标准输入
  • 创建要在本地机器上运行的所有命令,然后将预先准备好的命令传递给远程机器,例如ssh .... "$(for ...; do; echo curl ...; done)"

我会选择第二个选项,因为我喜欢使用管道传递所有内容,并且不喜欢在我之后进行清理 - 如果出现错误,删除临时文件可能会很麻烦。

我的脚本可能如下所示:

#!/bin/bash 
set -euo pipefail
read -r host _ <webip.txt
paste serial.txt ip.txt | ssh -t -o StrictHostKeyChecking=no -p 2345 -v ubuntu@"$host" '#!/bin/bash
set -euo pipefail
while read -r no_n ip; do
     for ((i = 0; i < no_n; ++i)); do
         printf "%s\n" "$no_n"
         printf "%s\n" "$ip"
         curl -fIkSs https://"$ip":9002 | head -n 1 
      done
done
'

由于远程脚本会变得更大且对 qouting 不太友好,我会将其保存到另一个 remote_scripts.sh 并执行 ssh ... -m remote_scripts.sh
我不明白你想用 sudo su 做什么,它 100% 没有做你想做的事。
如果 no_n 幻数是执行该 curl 的次数,并且您有 xargs 并且您并不真正关心错误,那么您可以做一个神奇且令人困惑的 oneliner:

#!/bin/bash 
set -euo pipefail
read -r host _ <webip.txt
paste serial.txt ip.txt | ssh -t -o StrictHostKeyChecking=no -p 2345 -v ubuntu@"$host" 'xargs -n2 -- sh -c "seq 0 \"\$1\" | xargs -n1 -- sh -c \"curl -fIkSs https://\\\"\\\$1\\\":9002 | head -n 1\" -- \"\$2\"" --'

准备所有要运行的命令实际上可能更具可读性,并且可以节省一些讨厌的 qouting 来解决。但这实际上取决于 serial.txt 和 ip.txt 有多大以及要在远程机器上执行的命令有多大,因为您希望最大限度地减少机器之间传输的字节数。
这里要运行的命令是在本地机器上构建的(即“$(...)”被传递给 ssh)并在远程机器上执行:

# semi-readable script, not as fast and no xargs
ssh -t -o StrictHostKeyChecking=no -p 2345 -v ubuntu@"$host" "$(paste serial.txt ip.txt | while read -r serial ip; do 
   seq 0 "$serial" | while read -r _; do
         echo "curl -fIkSs \"https://$ip:9002\" | head -n 1"
   done

完成)”

HERE-doc 不扩展 shell 命令,所以:

$ cat <<EOF
> echo 1
> EOF
echo 1

但你可以使用命令替换$( ... ):

$ cat <<EOF
> $(echo 1)
> EOF
1

【讨论】:

  • 由于时间紧迫,我没有尝试过解决方案,我使用了答案中提到的paste 命令,这节省了我的时间。将在不久的将来实施这一点。感谢所有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-01
  • 1970-01-01
  • 2014-11-23
  • 1970-01-01
  • 1970-01-01
  • 2019-01-13
  • 1970-01-01
相关资源
最近更新 更多