bash Bash而读取循环提前中断

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/346445/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 17:49:38  来源:igfitidea点击:

Bash while read loop breaking early

bashshell

提问by Foo Bah

I've been banging my head against for wall for a while with this one.

我一直在用这个头撞墙一段时间。

I want to SSH into a set of machines and check whether they are available (accepting connections and not being used). I have created a small script, tssh, which does just that:

我想通过 SSH 连接到一组机器并检查它们是否可用(接受连接但未被使用)。我创建了一个小脚本 tssh,它就是这样做的:

#!/bin/bash

host=
timeout=${2:-1}

ssh -qo "ConnectTimeout $timeout" $host "[ \`who | cut -f1 | wc -l \` -eq 0 ] && exit 0 || exit 1"

This script works correctly. Returning 255 if there was a connection problem, 1 if the machine is busy and 0 if everything is good. If anyone knows a better way to do this please let me know.

此脚本正常工作。如果存在连接问题,则返回 255,如果机器忙则返回 1,如果一切正常则返回 0。如果有人知道更好的方法来做到这一点,请告诉我。

So next I try and call tssh on my set of machines using a while read loop, and this is where it all goes wrong. The loop exits as soon as tssh returns 0 and never completes the full set.

所以接下来我尝试使用 while 读取循环在我的一组机器上调用 tssh,这就是一切出错的地方。一旦 tssh 返回 0 并且永远不会完成完整集,循环就会退出。

while read nu ; do tssh "MYBOXES$nu" ; done < <(ruby -e '(0..20).each { |i| puts i }')

At first I thought this was a subshell problem but apparently not. Any help, along with comments on style/content, would be much appreciated! I know I'm going to kick myself when I find out why...

起初我认为这是一个子外壳问题,但显然不是。任何帮助,以及对样式/内容的评论,将不胜感激!我知道当我发现原因时我会踢自己...

采纳答案by Paul Tomblin

Don't know if it would help, but a cleaner way of writing that would be

不知道它是否会有所帮助,但更简洁的写作方式是

for nu in `ruby -e '(0..20).each { |i| puts i}'`; do
  tssh "MYBOXES$nu" 
done

回答by

Chris is correct. The source of the loop breaking was SSH using stdin, however guns is correct in is usage of a looping methodology.

克里斯是对的。循环中断的来源是使用标准输入的 SSH,但是 guns 在使用循环方法方面是正确的。

If you are looping through input (a file with a list of hostnames for example), and calling SSH, you need to pass the -n parameter, otherwise your loop based on input will fail.

如果您循环输入(例如带有主机名列表的文件)并调用 SSH,则需要传递 -n 参数,否则基于输入的循环将失败。

while read host; do
  ssh -n $host "remote command" >> output.txt
done << host_list_file.txt

回答by Foo Bah

In the construct

在构造

something | 
while read x; do 
    ssh ...
done

the standard input as seen by the while loop is the output of something.

while 循环所看到的标准输入是 的输出something

The default behavior of sshis to read standard input. This allows you to do things like

的默认行为ssh是读取标准输入。这使您可以执行以下操作

cat id_rsa.pub | ssh new_box "cat - >> ~/.ssh/authorized_keys"

Now, with that being said, when the first value is read, the first ssh command will read the entire input from something. Then, by the time ssh finishes, there is no output left, and readstops.

现在,话虽如此,当读取第一个值时,第一个 ssh 命令将从something. 然后,到 ssh 完成时,没有输出,并read停止。

The fix is ssh -n ...e.g.

修复是ssh -n ...例如

cat /etc/hosts | awk '{print }' | while read x; do
    ssh -n $x "do_something_on_the_machine"
done

回答by Foo Bah

I ran into this today -- rsh and/or ssh can break a while read loop due to it using stdin. I put a -n into the ssh line which stops it from trying to use stdin and it fixed the problem.

我今天遇到了这个问题——rsh 和/或 ssh 可能会因为使用 stdin 而中断 while 读取循环。我将 -n 放入 ssh 行,阻止它尝试使用 stdin 并解决了问题。

回答by rouble

Most of the answers are specific to ssh. Other commands also hiHyman stdin and do not have a -n option. This should address any other commands. This should also work for ssh.

大多数答案都特定于 ssh。其他命令也会劫持 stdin 并且没有 -n 选项。这应该解决任何其他命令。这也适用于 ssh。

while read x; do 
    # Make sure command does not hiHyman stdin
    echo "" | command $x
done < /path/to/some/file

回答by Johannes Schaub - litb

I'm also unsure about why it fails, but i like xargsand seq:

我也不确定它为什么会失败,但我喜欢xargsseq

seq 0 20 | xargs -n1 tssh MYBOXES

回答by guns

As Kaii mentioned, it's really overkill to call ruby or seq (which won't work on BSD or OSX machines) just to output a range of numbers. If you're happy with using bash you can:

正如 Kaii 所提到的,仅仅为了输出一系列数字而调用 ruby​​ 或 seq(这不适用于 BSD 或 OSX 机器)真的有点矫枉过正。如果您对使用 bash 感到满意,您可以:

for i in {0..20}; do
    # command
done

I believe this should work for bash 2.05b and up.

我相信这应该适用于 bash 2.05b 及更高版本。

回答by Kevin Mullet

talk about a "robbing Peter to pay Paul" problem. I'd been struggling for hours to figure out why my ssh was killing my something|while read loop.

讲一个“抢彼得付保罗”的问题。我一直在努力弄清楚为什么我的 ssh 会杀死我的东西|while read 循环。

Another way to stick with a while read loop and keep your ssh at the same time is to use the "-n" switch to make STDIN on ssh /dev/null. Works like a charm for me:

坚持使用 while 读取循环并同时保持 ssh 的另一种方法是使用“-n”开关在 ssh /dev/null 上创建 STDIN。对我来说就像一个魅力:

#!/bin/bash
[...]
something|while read host
   do
      ssh -nx ${host} fiddleAround
   done

(I tend to always use the "-x" too to avoid wasting time negotiating X in a tunnel.)

(我也倾向于总是使用“-x”以避免浪费时间在隧道中协商 X。)

回答by Kaii

i cant believe it was the result of 0 that broke your loop, you can test against this by replacing your tssh command in the loop with "/bin/true" which also returns 0.

我不敢相信这是 0 的结果破坏了您的循环,您可以通过将循环中的 tssh 命令替换为“/bin/true”(也返回 0)来对此进行测试。

regarding style i dont understand why a simple looping shell script needs ruby, perl, seq or jot or any other binary that is not on my *BSD.

关于样式,我不明白为什么一个简单的循环 shell 脚本需要 ruby​​、perl、seq 或 jot 或任何其他不在我的 *BSD 上的二进制文件。

you can alternatively use the shells builtin for loop construct, which works at least in ksh, bash:

您也可以使用内置的 shell for 循环构造,它至少适用于 ksh、bash:

for ((i=0; $i<=20; i++)); do
    tssh "MYBOXES$i"
done