while循环只处理ssh命令的第一个条目

所以我对bash不太了解,需要一些亲的帮助。 我正在尝试运行像这样的脚本:

filename='file1' while read p; do ssh -p 2222 $p 'who -b' | awk '{print $(NF-1)" "$NF}' >> file2* 

我想要做的是一个脚本,它遍历file1中的所有地址,以查看它们上次重新启动的时间,然后是file2中的答案。

问题是它只通过第一个地址而不是另一个地址。

第一个地址获得了我需要键入的密码以继续该过程。 可能这是问题,还是我在file1中指定了每一行,或者我开始时做的绝对错误?

最后我假设,脚本的其余部分没问题。 然后我跟着@ dessert的评论并使用shellcheck引导我了解实际问题及其解决方案:

SC2095 :添加< /dev/null以防止ssh吞下stdin。

所以你必须以这种方式改变你的脚本:

 ssh -p 2222 "$p" 'who -b' < /dev/null | awk '{print $(NF-1)" "$NF}' >> 'file2' 

根据原始答案并感谢@EliahKagan和@rexkogitans的评论中提供的有用建议,完整的脚本可能如下所示:

 #!/bin/bash # Collect the user's input, and if it`s empty set the default values [[ -z "${1}" ]] && OUT_FILE="reboot-indication.txt" || OUT_FILE="$1" [[ -z "${2}" ]] && IN_FILE="hosts.txt" || IN_FILE="$2" while IFS= read -r host; do indication="$(ssh -n "$host" 'LANG=C who -b' | awk '{print $(NF-1)" "$NF}')" printf '%-14s %s\n' "$host" "$indication" >> "$OUT_FILE" done < "$IN_FILE" 
  • < /dev/null/ssh命令的-n选项替换。 来自man ssh

     -n Redirects stdin from /dev/null (actually, prevents reading from stdin). This must be used when ssh is run in the background... This does not work if ssh needs to ask for a password or passphrase; see also the -f option. 
  • IFS= read -r line - 正如@StéphaneChazelas在他的百科全书式回答中所说的那样 - 是read builtin读取一行输入的规范方法

    • 关键是读取来自(可能是反斜杠 - 续)行的单词,其中单词是$IFS分隔,反斜杠可用于转义分隔符(或继续行)。 所以应该调整read命令来读取行。

    • IFS=内部字段分隔符更改为空字符串 ,因此我们在结果中保留前导和尾随空格。

    • 选项-r - r aw输入 - 禁用读取数据( 引用 )中反斜杠转义和行继续的解释。

  • printf '%s %s' "$VAR1" "$VAR2"将提供更好的输出格式( 参考 )。

  • LANG=C将保证每个服务器上who -b输出相同,因此也可以保证使用awk解析输出。

  • 注意这里假设有~/.ssh/config文件和不需要-p 2222附加参数( 参考 )。


调用上面的ssh-check.sh (不要忘记chmod +x )并以这种方式使用它:

  • 使用输入( hosts.txt )和输出( reboot-indication.txt )文件的默认值:

     ./ssh-check.sh 
  • 设置输出文件的自定义值; 为输入文件设置自定义值:

     ./ssh-check.sh 'my-custom.out' ./ssh-check.sh 'my-custom.out' 'my-custom.in' 

阅读此答案 ,了解如何改进整个方法。

你忘了关闭while-do循环。 添加done到最后。

 filename='file1' while read p; do ssh -p 2222 $p 'who -b' | awk '{print $(NF-1)" "$NF}' >> file2* done