#!/usr/bin/env ruby
require 'rubygems'
require 'pty'
require 'expect'
require 'io/console'
hosts_file = ARGV[0] || "hosts"
print "Password:"
password = $stdin.noecho(&:gets)
password.chomp!
puts
$expect_verbose = true
File.open(hosts_file).each do |host|
host.chomp!
print "Copying id to #{host} ... "
begin
PTY.spawn("ssh-copy-id #{host}") do |cp_out, cp_in, pid|
begin
pattern = /#{host}'s password:/
cp_out.expect(pattern, 10) do |m|
cp_in.printf("#{password}\n")
end
cp_out.readlines
rescue Errno::EIO
ensure
Process.wait(pid)
end
end
rescue PTY::ChildExited => e
puts "Exited: #{e.status}"
end
status = $?
if status == 0
puts "Done!"
else
puts "Failed with exit code #{status}!"
end
end
Thursday, December 27, 2012
Have to read all from Ruby PTY output
I wrote a Ruby script to call ssh-copy-id to distribute my public key to a list of hosts. It took me a while to make it work. The code is simple: read the password, and call ssh-copy-id for each host to copy the public key. Of course, the code doesn't consider all scenarios like the key is already copied, and wrong password. The tricky parts are "cp_out.readlines" and "Process.wait(pid)". If you don't read all the data from cp_out (comment out cp_out.readlines), the spawned process won't return.
Subscribe to:
Post Comments (Atom)
I've spent the last two hours trying to figure out why my code wasn't working. Turns out my exception handling was in the wrong place. Thanks to your code and lines 28 and 29, I suddenly realised why it was failing.
ReplyDeleteThank you so much.