Команды Unix работают на сервере, но не в сессии ruby ssh
Я пытаюсь научиться использовать жемчужину net-ssh для ruby. Я хочу выполнить приведенные ниже команды после входа в каталог -/home/james.
cd /
pwd
ls
Когда я делаю это со шпателем, он работает, и я могу видеть список каталогов. Но, когда я делаю это с кодом ruby, он не дает мне тот же результат.
require 'rubygems'
require 'net/ssh'
host = 'server'
user = 'james'
pass = 'password123'
def get_ssh(host, user, pass)
ssh = nil
begin
ssh = Net::SSH.start(host, user, :password => pass)
puts "conn successful!"
rescue
puts "error - cannot connect to host"
end
return ssh
end
conn = get_ssh(host, user, pass)
def exec(linux_code, conn)
puts linux_code
result = conn.exec!(linux_code)
puts result
end
exec('cd /', conn)
exec('pwd', conn)
exec('ls', conn)
conn.close
Выход -
conn successful!
cd /
nil
pwd
/home/james
ls
nil
Я ожидал, что pwd даст мне/вместо/home/james. Так оно и работает в замазке. Какая ошибка в рубиновом коде?
Ответы
Ответ 1
Кажется, что каждая команда запускается в своей собственной среде, поэтому текущий каталог не переносится на exec в exec. Вы можете проверить это, если:
exec('cd / && pwd', conn)
Он напечатает /
. Из документации не ясно, как заставить все команды выполняться в одной и той же среде или если это вообще возможно.
Ответ 2
Это связано с тем, что net/ssh
не имеет состояния, поэтому он открывает новое соединение при выполнении каждой команды.
Вы можете использовать rye gem, который реализует для этого работу. но я не знаю, работает ли он с ruby > 2, так как его разработка не так активна.
Другим способом является использование процесса pty, в котором вы откроете псевдотерминал с помощью команды ssh
, чем используйте входные и выходные файлы для записи команд для терминала и чтения результатов. Чтобы прочитать результаты, вам необходимо использовать метод select класса IO. Но вам нужно научиться использовать эти утилиты, поскольку это не так очевидно для не опытного программиста.
Ответ 3
И, Эй, я нашел, как это сделать, и на самом деле это так просто. Я думаю, что в последний раз я не дошел до этого решения, потому что был немного знаком с этой штукой net-ssh, pty terminal. Но я, наконец, нашел это, и вот и пример.
require 'net/ssh'
shell = {} #this will save the open channel so that we can use it accross threads
threads = []
# the shell thread
threads << Thread.new do
# Connect to the server
Net::SSH.start('localhost', 'your_user_name', password: 'your_password') do |session|
# Open an ssh channel
session.open_channel do |channel|
# send a shell request, this will open an interactive shell to the server
channel.send_channel_request "shell" do |ch, success|
if success
# Save the channel to be used in the other thread to send commands
shell[:ch] = ch
# Register a data event
# this will be triggered whenever there is data(output) from the server
ch.on_data do |ch, data|
puts data
end
end
end
end
end
end
# the commands thread
threads << Thread.new do
loop do
# This will prompt for a command in the terminal
print ">"
cmd = gets
# Here you've to make sure that cmd ends with '\n'
# since in this example the cmd is got from the user it ends with
#a trailing eol
shell[:ch].send_data cmd
# exit if the user enters the exit command
break if cmd == "exit\n"
end
end
threads.each(&:join)
и здесь мы являемся интерактивным терминалом, использующим жемчужину net-ssh ruby.
Для получения дополнительной информации смотрите здесь для предыдущей версии 1, но вам так полезно понять, как работает каждая деталь. И здесь