ruby中文件内容的循环

ruby中文件内容的循环,ruby,Ruby,好的,我是Ruby新手,在bash/ksh/sh方面有很强的背景 我试图做的是使用一个简单的for循环在多个服务器上运行一个命令。在bash中,我会这样做: for SERVER in `cat etc/SERVER_LIST` do ssh -q ${SERVER} "ls -l /etc" done etc/SERVER\u列表只是一个如下所示的文件: server1 server2 server3 etc 在Ruby中我似乎无法正确理解这一点。这就是我到目前为止所做的: #!

好的,我是Ruby新手,在bash/ksh/sh方面有很强的背景

我试图做的是使用一个简单的for循环在多个服务器上运行一个命令。在bash中,我会这样做:

for SERVER in `cat etc/SERVER_LIST`
do
    ssh -q ${SERVER} "ls -l /etc"
done
etc/SERVER\u列表只是一个如下所示的文件:

server1
server2
server3
etc
在Ruby中我似乎无法正确理解这一点。这就是我到目前为止所做的:

 #!/usr/bin/ruby
### SSH testing
#
#

require 'net/ssh'

File.open("etc/SERVER_LIST") do |f|
        f.each_line do |line|
                Net::SSH.start(line, 'andex') do |ssh|
                        result = ssh.exec!("ls -l")
                        puts result
                end
        end
end
我现在得到这些错误:

andex@master:~/sysauto> ./ssh2.rb
/usr/lib64/ruby/gems/1.8/gems/net-ssh-2.0.23/lib/net/ssh/transport/session.rb:65:in `initialize': newline at the end of hostname (SocketError)
        from /usr/lib64/ruby/gems/1.8/gems/net-ssh-2.0.23/lib/net/ssh/transport/session.rb:65:in `open'
        from /usr/lib64/ruby/gems/1.8/gems/net-ssh-2.0.23/lib/net/ssh/transport/session.rb:65:in `initialize'
        from /usr/lib64/ruby/1.8/timeout.rb:53:in `timeout'
        from /usr/lib64/ruby/1.8/timeout.rb:93:in `timeout'
        from /usr/lib64/ruby/gems/1.8/gems/net-ssh-2.0.23/lib/net/ssh/transport/session.rb:65:in `initialize'
        from /usr/lib64/ruby/gems/1.8/gems/net-ssh-2.0.23/lib/net/ssh.rb:179:in `new'
        from /usr/lib64/ruby/gems/1.8/gems/net-ssh-2.0.23/lib/net/ssh.rb:179:in `start'
        from ./ssh2.rb:10
        from ./ssh2.rb:9:in `each_line'
        from ./ssh2.rb:9
        from ./ssh2.rb:8:in `open'
        from ./ssh2.rb:8

文件来源正确,我使用的是相对路径,因为我位于etc/(不是/etc,我在脚本目录中运行此命令,在脚本目录中,我将文件保存在名为etc的子目录中。)

对文件进行逐行迭代时,我看到的最常见结构是:

File.open("etc/SERVER_LIST") do |f|
    f.each_line do |line|
      # do something here
    end
end
要用一些更一般的Ruby信息来扩展上面的内容。。。此语法相当于:

File.open("etc/SERVER_LIST") { |f|
    f.each_line { |line|
      # do something here
    }
}
当我第一次接触Ruby时,我不知道| f |和| line |语法是什么意思。我知道什么时候使用它,它是如何工作的,但不知道他们为什么选择这种语法。在我看来,这是Ruby的神奇之处之一。上面这种简单的语法实际上在你的眼皮底下隐藏着一个非常高级的编程概念。嵌套在“do”/“end”或{}中的代码称为块。你可以认为它是匿名函数或lambda。|f |和| line |语法实际上只是执行父级传递给代码块的参数的句柄

对于File.open(),匿名函数只接受一个参数,即underyling File IO对象的句柄

在每个_线的情况下,这是一个interator函数,每行调用一次。| line |只是函数每次迭代时传递的数据的变量句柄

哦,关于do/end with File.open的一个好处是它会在结尾自动关闭文件

编辑:

现在出现的错误表明SSH调用不喜欢字符串末尾的额外空格(换行符)。要解决此问题,只需执行以下操作

Net::SSH.start(line.strip, 'andex') do |ssh|
end

使用
File.foreach

require 'net/ssh'

File.foreach('etc/SERVER_LIST', "\n") do |line|
        Net::SSH.start(line, 'andex') do |ssh|
          result = ssh.exec!("ls -l")
          puts result
        end
end
第一行打开文件进行读取,并立即进入一个块。(块是
do
end
之间的代码。也可以用
{
}
包围块。经验法则是
do..end
用于多行块,
{…}
用于单行块。)块在Ruby中非常常见。远比
while
for
循环更惯用。)调用
open
会自动接收文件句柄,并在管道中为其命名

一旦你掌握了它,可以说,你可以调用它上面的每一行,然后像数组一样迭代它。同样,每次迭代都会自动向您传递一行,您可以在管道中随意调用该行

这种方法的好处在于,它可以省去在完成文件时关闭文件的麻烦。以这种方式打开的文件将在您离开外部块时自动关闭

还有一件事:几乎可以肯定文件名为
/etc/SERVER\u LIST
。您需要初始的
/
来指示文件系统的根目录(除非您有意使用文件路径的相对值,我对此表示怀疑)。仅此一点就可能使您无法打开文件

新错误的更新
Net::SSH
正在换行符上呕吐。如果你有这个:

 Net::SSH.start(line, 'andex') do |ssh|
这样做:

 Net::SSH.start(line.chomp, 'andex') do |ssh|

chomp
方法从字符串中删除任何最后的换行符。

从文件中读取行是一种常见的操作,Ruby有一种简单的方法可以做到这一点:

servers = File.readlines('/etc/SERVER_LIST')
readlines
方法将打开文件,将文件读入数组,然后为您关闭文件(因此您不必担心这些)。变量
servers
将是一个字符串数组;每个字符串都是文件中的一行。您可以使用
Array::each
方法迭代此数组并使用已有的代码。试试这个:

servers = File.readlines('/etc/SERVER_LIST')
servers.each {|s|
    Net::SSH.start(s, 'andex') {|ssh| puts ssh.exec!("ls -l") }
}

我想这就是“初始化”中的
:主机名末尾的换行符(SocketError)
错误:

Net::SSH.start(line.chomp,'andex')


each_line方法包括“\n”,chomp将删除它。

除了其他人所说的,我只想指出for循环在Ruby中没有太多使用。
each
方法的使用频率更高。添加
chomp
以删除换行符。我将更新我的答案。使用line.strip删除字符串中的空白。用这个附录更新了我下面的答案,我将指出在bash中有更好的方法:
在读取服务器时;做完成
很好的解释。我只想补充一点,尽管
每一行
都更加明确(正如您在文件对象上调用它一样),但是常规的
每一行
也可以工作。我倾向于使用较少的类型,它提醒我一个文件可以被视为一个行数组。事实上,还有一件事:这是一个惯例,不是一个规则,但对于多行块,
{…}
在我看来确实是错误的。@Telemachus是的,同意多行的-do/end和单行的{}似乎是潜标准。单行do/end比多行括号更难看!编辑:在较新版本的Ruby中,
每个
都不再工作。您需要显式地调用
每一行
。所以不要理会我(现在)的坏建议。你可以这样做,但我不会。首先,如果文件非常大,那么
readlines
将对内存造成很大的影响。其次,在这种情况下,不需要创建像
服务器那样的数组。只需将文件输入一个块(就像我们中的一些人在其他答案中所做的那样),并在获得行时对其进行操作。不需要永久数组。@Telemachus-因为他提到他是一个Ruby新手,所以我选择了一种最容易阅读和理解的方法(只有一种)
servers = File.readlines('/etc/SERVER_LIST')
servers.each {|s|
    Net::SSH.start(s, 'andex') {|ssh| puts ssh.exec!("ls -l") }
}