2012-03-04 58 views
6

我試圖抓取FTP並遞歸地拉下所有文件。Ruby 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 

但事實證明,如果拆分列表,直到最後一個空格,文件名和目錄的空間被取出錯誤的。 這裏需要一些關於邏輯的幫助。

回答

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]

+0

您也可以使用'entry [1 ..- 1] .split [5] [13 ..- 1]'得到路徑和'entry [0]'來獲得類型 – 2012-03-04 12:18:43

+0

這個正則表達式是否適用於所有案件?周圍有各種各樣的FTP服務器。我們有客戶使用一些模糊的專有,基於Windows的服務器,並且他們返回的文件列表與Linux版本完全不同。所以我最終做的是爲每個文件/目錄條目嘗試使用CD,如果這不起作用 - 請將其視爲一個文件:) 工程就像一個魅力。 – 2012-04-10 16:24:14

4

你能避免遞歸,如果你列出所有文件一次

files = ftp.nlst('**/*.*')

目錄不包括在列表中,但完整的FTP路徑仍然是可用的名稱。

編輯

我假設每個文件名包含一個圓點和目錄名沒有。感謝提@Niklas B.

+0

現在試圖在更深的遞歸中實現他,謝謝。 – Norris 2012-03-04 10:33:17

+1

我認爲這至少可以假設每個文件都有一個點。我不確定它是否使第二個假設是目錄*不在其中有點。無論哪種方式,我認爲案文至少應該提到這一事實。 – 2012-03-04 11:57:45

2

周圍有各種各樣的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

假設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]包含目錄(假定被檢查的行實際上表示一個目錄)的名稱。

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 
相關問題