Question: I'm trying to crawl FTP and pull down all the files recursively. 

Up until now I was trying to pull down a directory with 

```
   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

```

But turns out, if you split the list up until last space, filenames and directories with spaces are fetched wrong.
Need a little help on the logic here.

Answer: 
There are a huge variety of FTP servers around.

We have clients who use some obscure proprietary, Windows-based servers and the file listing returned by them look completely different from Linux versions.

So what I ended up doing is for each file/directory entry I try changing directory into it and if this doesn't work - consider it a file :)

The following method is "bullet proof":

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

```

Works like a charm, but **please note:** if the FTP directory has tons of files in it - this method **takes a while** to traverse all of them.
As @Alex pointed out, using patterns in filenames for this is hardly reliable. Directories CAN have dots in their names (.ssh for example), and listings can be very different on different servers. 

His method works, but as he himself points out, takes too long. 
I prefer using the .size method from Net::FTP. 
It returns the size of a file, or throws an error if the file is a directory. 

```
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

```