从ruby中的文件内容循环

时间:2010-07-07 20:08:37

标签: ruby

好的,所以我是Ruby的新手,我在bash / ksh / sh中有很强的背景知识。

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

for SERVER in `cat etc/SERVER_LIST`
do
    ssh -q ${SERVER} "ls -l /etc"
done

etc / SERVER_LIST只是一个看起来像这样的文件:

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,我是在脚本目录中运行的,我将文件保存在一个名为的子目录中等)

5 个答案:

答案 0 :(得分:27)

File.open("/etc/SERVER_LIST", "r") do |file_handle|
  file_handle.each_line do |server|
    # do stuff to server here
  end
end

第一行打开文件进行读取,然后立即进入一个块。 (该块是doend之间的代码。您还可以使用{}来围绕块。经验法则为do..end -line块和{...}用于单行块。)块在非常在Ruby中很常见。比whilefor循环更加惯用。)对open的调用会自动接收文件句柄,并在管道中为其命名。

一旦掌握了这一点,可以说,你可以在其上调用each_line,并像对待数组一样迭代它。同样,每次迭代都会自动向您传递一行,您可以在管道中调用您喜欢的行。

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

另一件事:该文件几乎肯定被命名为/etc/SERVER_LIST。您需要初始/来指示文件系统的根目录(除非您故意使用文件路径的相对值,我怀疑)。仅这一点可能使您无法打开文件。

更新新错误Net::SSH正在换行。你有这个:

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

这样做:

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

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

答案 1 :(得分:11)

使用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

答案 2 :(得分:4)

我在进行文件的在线迭代时看到的最常见的构造是:

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(),匿名函数采用单个参数,该参数是底层文件IO对象的句柄。

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

哦,关于使用File.open执行/结束的一个好处是它会自动关闭文件。

编辑:

您现在得到的错误表明SSH调用不理解字符串末尾的额外空格(换行符)。要解决此问题,只需执行

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

答案 3 :(得分:1)

从文件中读取行是一种常见的操作,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") }
}

答案 4 :(得分:1)

我认为这是你想要的in 'initialize': newline at the end of hostname (SocketError)错误:

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

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