2012-03-04 11 views
6

Estoy intentando rastrear FTP y extraer todos los archivos recursivamente.Ruby FTP Separación de archivos de las carpetas

Hasta ahora yo estaba tratando de derribar un directorio con

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 

Pero resulta que, si se divide la lista hasta último espacio, los nombres de archivos y directorios con espacios están equivocados descabellada. Necesito un poco de ayuda sobre la lógica aquí.

Respuesta

2

También puede usar una expresión regular. Yo puse uno juntos. Por favor verifique si funciona para usted y no sé si su directorio aparecerá diferente. Tienes que usar Ruby 1.9 por cierto.

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) 

que son capaces de acceder a los elementos por su nombre a continuación

match[:type] contiene una 'd' si se trata de un directorio, un espacio si se trata de un archivo.

Todos los demás elementos están también allí. Lo más importante es match[:path].

+0

También podría usar 'entrada [1 ..- 1] .split [5] [13 ..- 1]' para obtener la ruta y 'entrada [0]' para obtener el tipo –

+0

¿Funciona esta expresión regular para todos ¿casos? Hay una gran variedad de servidores FTP alrededor. Tenemos clientes que utilizan algunos servidores oscuros basados ​​en Windows y la lista de archivos que devuelven se ve completamente diferente de las versiones de Linux. Entonces, lo que terminé haciendo es por cada entrada de archivo/directorio en la que intento introducir CD y si esto no funciona, considérelo como un archivo :) Funciona como un encanto. –

4

Usted puede evitar la repetición si la lista de todos los archivos a la vez

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

directorios no están incluidos en la lista, pero la ruta FTP completo todavía está disponible en el nombre.

EDITAR

Estoy asumiendo que cada nombre de archivo contiene un punto y nombres de directorio no lo hacen. Gracias por mencionar @Niklas B.

+0

Tratando de implementar su en una recursión más profunda en este momento, gracias. – Norris

+1

Creo que esto hace por lo menos la suposición de que cada archivo tiene un punto. No estoy seguro de si hace la segunda suposición de que los directorios * no * tienen puntos en ellos. De cualquier manera, creo que el texto debería al menos mencionar ese hecho. –

2

Hay una gran variedad de servidores FTP.

Tenemos clientes que usan algunos servidores oscuros propietarios basados ​​en Windows y la lista de archivos devuelta por ellos se ve completamente diferente de las versiones de Linux.

Así que lo que terminó haciendo es para cada entrada del archivo/directorio que intente cambiar el directorio en él y si esto no funciona - consideran que es un archivo :)

El siguiente método es "a prueba de balas":

# 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)} 

funciona como un encanto, pero favor nota: si el directorio FTP tiene un montón de archivos en ella - este método toma un tiempo para atravesar todos ellos.

2

Suponiendo que el servidor FTP devuelve Unix-like listas de archivos, el siguiente código funciona. Al menos para mi.

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] contiene el nombre del directorio (teniendo en cuenta que la línea de inspección en realidad representa un directorio).

0

Como señaló @Alex, el uso de patrones en los nombres de archivo para esto no es confiable. Los directorios PUEDEN tener puntos en sus nombres (.ssh por ejemplo), y los listados pueden ser muy diferentes en diferentes servidores.

Su método funciona, pero como él mismo señala, lleva demasiado tiempo. Prefiero usar el método .size de Net :: FTP. Devuelve el tamaño de un archivo o arroja un error si el archivo es un directorio.

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 
Cuestiones relacionadas