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.
I'll add my solution to the mix...

Using `ftp.nlst('**/*.*')` did not work for me... server doesn't seem to support that \*\* syntax.

The chdir trick with a `rescue` seems expensive and hackish.

Assuming that all files have at least one char, a single period, and then an extension, I did a simple recursion.

```
  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` seems to return the full path to whatever it finds *non-recursively*... so each time you get a listing, separate the files from the folders, and then process any folder you find recrsively. Collect all the file results.

To call, you can pass a starting folder

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

```