Цикл из содержимого файла в ruby - PullRequest
9 голосов
/ 08 июля 2010

Хорошо, я новичок в Ruby и у меня хороший опыт работы с bash / ksh / sh.

Я пытаюсь использовать простой цикл for для запуска команды на нескольких серверах.В bash я бы сделал это следующим образом:

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

и т. Д. / 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, я запускаю его из каталога сценариев, где я храню файл в подкаталоге с именем etc.)

Ответы [ 5 ]

27 голосов
/ 08 июля 2010
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 для многострочных блоков и {...} для однострочныхблоки строк.) Блоки очень распространены в 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 удаляет все последние символы новой строки из строки.

11 голосов
/ 08 июля 2010

Использование 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
4 голосов
/ 08 июля 2010

Самая распространенная конструкция, которую я вижу при выполнении итерации файла:

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 | и | линия | синтаксис имел ввиду. Я знал, когда его использовать и как это работает, но не знал, почему они выбрали этот синтаксис. На мой взгляд, это одна из волшебных вещей в Ruby. Этот простой синтаксис выше на самом деле скрывает очень продвинутую концепцию программирования прямо под вашим носом. Код, вложенный в «do» / «end» или {}, называется блоком. И вы можете считать это анонимной функцией или лямбда-выражением. | F | и | линия | на самом деле синтаксис - это всего лишь дескриптор параметра, передаваемого блоку кода исполняющим родителем.

В случае File.open () анонимная функция принимает один аргумент, который является дескриптором подчиненного объекта File IO.

В случае each_line это функция-интегратор, которая вызывается один раз для каждой строки. Линия | это просто дескриптор переменной для данных, которые передаются с каждой итерацией функции.

О, и есть одна приятная вещь в do / end для File.open - он автоматически закрывает файл в конце.

Edit:

Ошибка, которую вы получаете сейчас, предполагает, что вызов SSH не оценивает лишние пробелы (перевод строки) в конце строки. Чтобы это исправить, просто сделайте

Net::SSH.start(line.strip, 'andex') do |ssh|
end
2 голосов
/ 08 июля 2010

Чтение строк из файла - обычная операция, и у 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") }
}
1 голос
/ 08 июля 2010

Я думаю, это то, что вы хотите для ошибки in 'initialize': newline at the end of hostname (SocketError):

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

Метод each_line включает «\ n», и chomp удалит его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...