好的,所以我是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,我是在脚本目录中运行的,我将文件保存在一个名为的子目录中等)
答案 0 :(得分:27)
File.open("/etc/SERVER_LIST", "r") do |file_handle|
file_handle.each_line do |server|
# do stuff to server here
end
end
第一行打开文件进行读取,然后立即进入一个块。 (该块是do
和end
之间的代码。您还可以使用{
和}
来围绕块。经验法则为do..end
-line块和{...}
用于单行块。)块在非常在Ruby中很常见。比while
或for
循环更加惯用。)对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将删除它。