Writing a function that checks via SFTP if a file is present on the server.
I have written a function sftp_file_exists_1?
that works. Now I want to split this function into two functions, and to my surprise, this does not work.
require "net/sftp"
def sftp_file_exists_1?(host, user, filename)
Net::SFTP.start(host, user, verify_host_key: :always) do |sftp|
sftp.stat(filename) do |response|
return response.ok?
end
end
end
def sftp_stat_ok?(sftp, filename)
sftp.stat(filename) do |response|
return response.ok?
end
end
def sftp_file_exists_2?(host, user, filename)
Net::SFTP.start(host, user, verify_host_key: :always) do |sftp|
return sftp_stat_ok?(sftp, filename)
end
end
p sftp_file_exists_1?("localhost", "user", "repos")
p sftp_file_exists_2?("localhost", "user", "repos")
I expected:
true
true
since a file repos
actually exists on the server. However, I get (abbreviated):
true
#<Net::SFTP::Request:0x000055f3b56732d0 @callback=#<Proc:0x000055f3b5673280@./test.rb:14>, ...
Addendum: this works:
def sftp_stat_ok?(sftp, filename)
begin
sftp.stat!(filename)
rescue Net::SFTP::StatusException
return false
end
return true
end
Interesting problem.
Let's add some puts
to see what happens:
require "net/sftp"
def sftp_file_exists_1?(host, user, filename)
Net::SFTP.start(host, user, verify_host_key: :always) do |sftp|
puts " BEFORE STAT"
sftp.stat(filename) do |response|
puts " REQUEST FINISHED"
return response.ok?
end
puts " AFTER STAT"
end
puts " NOT EXECUTED"
end
def sftp_stat_ok?(sftp, filename)
request = sftp.stat(filename) do |response|
puts " REQUEST FINISHED"
return response.ok?
end
puts " REQUEST SENT"
request
end
def sftp_file_exists_2?(host, user, filename)
Net::SFTP.start(host, user, verify_host_key: :always) do |sftp|
puts " CALL STAT_OK?"
return sftp_stat_ok?(sftp, filename)
end
puts " NOT EXECUTED"
end
sftp_file_exists_1?
outputs:
BEFORE STAT
AFTER STAT
REQUEST FINISHED
true
While sftp_file_exists_2?
outputs:
CALL STAT_OK?
REQUEST SENT
#<Net::SFTP::Request:0x0000000001db2558>
" REQUEST FINISHED"
doesn't appear.
The block you pass to stat
is a callback. It only gets called when the server responds. To make sure the block gets executed before sftp_stat_ok?
returns, you need to wait for the request to be complete:
def sftp_stat_ok?(sftp, filename)
request = sftp.stat(filename) do |response|
return response.ok?
end
request.wait
end
def sftp_file_exists_2?(host, user, filename)
Net::SFTP.start(host, user, verify_host_key: :always) do |sftp|
return sftp_stat_ok?(sftp, filename)
end
end
It isn't needed for the first version because start
:
If a block is given, it will be passed to the SFTP session and will be called once the SFTP session is fully open and initialized. When the block terminates, the new SSH session will automatically be closed.