我正在尝试抓取FTP并递归下拉所有文件。
到目前为止,我试图用
下拉目录 ftp.list.each do |entry|
if entry.split(/\s+/)[0][0, 1] == "d"
out[:dirs] << entry.split.last unless black_dirs.include? entry.split.last
else
out[:files] << entry.split.last unless black_files.include? entry.split.last
end
但事实证明,如果将列表拆分到最后一个空格,则带有空格的文件名和目录将被取错。 在这里需要一点逻辑帮助。
答案 0 :(得分:4)
如果您一次列出所有文件,则可以避免递归
files = ftp.nlst('**/*.*')
目录不包含在列表中,但完整的ftp路径仍然可以在名称中使用。
修改
我假设每个文件名都包含一个点,目录名称不包含在内。谢谢你提到@Niklas B.
答案 1 :(得分:3)
周围有各种各样的FTP服务器。
我们的客户使用一些不起眼的专有的基于Windows的服务器,并且它们返回的文件列表与Linux版本完全不同。
所以我最终做的是每个文件/目录条目我尝试将目录更改为它,如果这不起作用 - 将其视为文件:)
以下方法是“防弹”:
# Checks if the give file_name is actually a file.
def is_ftp_file?(ftp, file_name)
ftp.chdir(file_name)
ftp.chdir('..')
false
rescue
true
end
file_names = ftp.nlst.select {|fname| is_ftp_file?(ftp, fname)}
像魅力一样工作,但请注意:如果FTP目录中包含大量文件 - 此方法需要一段时间来遍历所有文件。
答案 2 :(得分:2)
您还可以使用正则表达式。我把它放在一起。请验证它是否适合您,以及我不知道您的目录列表看起来不同。你必须使用Ruby 1.9 btw。
reg = /^(?<type>.{1})(?<mode>\S+)\s+(?<number>\d+)\s+(?<owner>\S+)\s+(?<group>\S+)\s+(?<size>\d+)\s+(?<mod_time>.{12})\s+(?<path>.+)$/
match = entry.match(reg)
您可以按名称访问元素
match[:type]
如果是目录则包含'd'
,如果是文件,则包含空格。
所有其他元素也在那里。最重要的是match[:path]
。
答案 3 :(得分:2)
假设FTP服务器返回类Unix 文件列表,则以下代码有效。至少对我而言。
regex = /^d[r|w|x|-]+\s+[0-9]\s+\S+\s+\S+\s+\d+\s+\w+\s+\d+\s+[\d|:]+\s(.+)/
ftp.ls.each do |line|
if dir = line.match(regex)
puts dir[1]
end
end
dir[1]
包含目录的名称(假设被检查的行实际上代表一个目录)。
答案 4 :(得分:0)
正如@Alex指出的那样,在文件名中使用模式并不可靠。目录可以在其名称中包含点(例如.ssh),并且列表在不同的服务器上可以非常不同。
他的方法有效,但正如他自己指出的那样,需要太长时间。 我更喜欢使用Net :: FTP中的.size方法。 它返回文件的大小,如果文件是目录则抛出错误。
def item_is_file? (item)
ftp = Net::FTP.new(host, username, password)
begin
if ftp.size(item).is_a? Numeric
true
end
rescue Net::FTPPermError
return false
end
end
答案 5 :(得分:0)
我会将我的解决方案添加到组合中...
使用 ftp.nlst('**/*.*')
对我不起作用...服务器似乎不支持 ** 语法。
带有 rescue
的 chdir 技巧看起来很昂贵而且很黑。
假设所有文件至少有一个字符、一个句点和一个扩展名,我做了一个简单的递归。
def list_all_files(ftp, folder)
entries = ftp.nlst(folder)
file_regex = /.+\.{1}.*/
files = entries.select{|e| e.match(file_regex)}
subfolders = entries.reject{|e| e.match(file_regex)}
subfolders.each do |subfolder|
files += list_all_files(ftp, subfolder)
end
files
end
nlst
似乎以非递归方式返回其找到的任何内容的完整路径...因此每次获得列表时,将文件与文件夹分开,然后处理任何您递归找到的文件夹。收集所有文件结果。
要调用,可以传入一个起始文件夹
files = list_all_files(ftp, "my_starting_folder/my_sub_folder")
files = list_all_files(ftp, ".")
files = list_all_files(ftp, "")
files = list_all_files(ftp, nil)