28

I feel like I am forgetting something obvious, and will remember or be ashamed as soon as an answer turns up. However, I cannot for the life of me get all of the directories in the current working directory. That is, given the following folder structure:

a
-c
b
-c

I would expect

FileNames["*",{"*"},Infinity]

to return

{"a","b","a/c","b/c"}

But instead I get

{"a/c","b/c"}

I tried playing around with Infinity in the typical Levels in Mathematica (such as {1} vs. 1), but MMA complains about syntax. For the above example, how can I view a complete list of subdirectories. I came up with the following, but it doesn't seem exactly elegant:

RecursiveDirectory[dir_] := 
 Module[{allDirs, allDirsCheck = {}, tempDirs},
  allDirs = Select[FileNames["*"], DirectoryQ[#] &];
  While[allDirs != allDirsCheck,
   allDirsCheck = allDirs;
   tempDirs = Select[FileNames["*", allDirs], DirectoryQ[#] &];
   allDirs = Union[allDirs, tempDirs]
   ];
  allDirs
  ]

This function gives me:

In[30]:= RecursiveDirectory["*"]
Out[30]= {"a", "a\\c", "b", "b\\c"}

As expected. What is a nicer way of doing this?

rm -rf
  • 88,781
  • 21
  • 293
  • 472
tkott
  • 4,939
  • 25
  • 44

4 Answers4

33

Maybe something like

Select[FileNames["*", "", Infinity], DirectoryQ]
Heike
  • 35,858
  • 3
  • 108
  • 157
  • 1
    Thanks Heike, I didn't think of trying an empty quote. I think I tried everything but that. And I knew the answer would be very simple too.... – tkott Feb 27 '12 at 21:05
  • One may also use FileType instead of DirectoryQ. – Szabolcs Sep 15 '17 at 10:35
17

If I'm not mistaken the second asterisk in your line

FileNames["*",{"*"},Infinity]

should be replaced with the location of the directory you want to look in.

In case of the directory where your notebook lives in this would be:

FileNames["*", {NotebookDirectory[]}, Infinity]

or, for the current directory, this would be:

FileNames["*", {Directory[]}, Infinity]
Sjoerd C. de Vries
  • 65,815
  • 14
  • 188
  • 323
  • Thanks Sjoerd. +1 for a detailed answer, but according to MMA.SE, Heike answered first (post 2389 vs. 2390) – tkott Feb 27 '12 at 21:06
  • 10
    @tkott This isn't a racing contest and please don't feel obliged to accept an answer the minute it arrives. That will discourage others to add their answers. – Sjoerd C. de Vries Feb 27 '12 at 21:24
14

I recommend an external command approach. On Windows this looks like this:

command = "!dir \"" <> Directory[] <> "\" /A:D /S /B";

ReadList[command, String]

This can be many times faster than the Select - FileNames method.

Select[
  FileNames["*", "C:\\Data & Images", Infinity],
  DirectoryQ
] // Length // AbsoluteTiming
{6.7413856, 5693}
command = "!dir \"" <> "C:\\Data & Images" <> "\" /A:D /S /B";
ReadList[command, String] // Length // AbsoluteTiming
{0.3900223, 5693}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 3
    I use ReadList often but had never discovered this. Good stuff! +1 – Andy Ross Feb 28 '12 at 06:26
  • 1
    @Andy thank you. I presume you mean the ability to read command line output? – Mr.Wizard Feb 28 '12 at 20:21
  • 2
    Yes, that is what I was referring to. I had a vague idea about using command line input in other places, I didn't realize it worked here as well. Potentially a very powerful idea. – Andy Ross Feb 28 '12 at 20:25
  • 1
    @Andy yes, it's surely cleaner than having to write to a temporary file, only to ReadList that. I think this should be better documented (present on the ReadList doc page). – Mr.Wizard Feb 28 '12 at 20:30
  • You should file that as a suggestion to WRI if you haven't already. – Andy Ross Feb 28 '12 at 20:35
1

If one needs to list only non-empty directories, it can be easily achieved by applying DirectoryName to the output of FileNames. For example, listing all non-empty directories in $UserBaseDirectory up to the depth 2:

DeleteDuplicates[DirectoryName /@ FileNames[All, $UserBaseDirectory, 2]]
{"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\ApplicationData\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\Applications\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\Autoload\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\FrontEnd\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\Kernel\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\Knowledgebase\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\_MyStyleSheets_\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\Paclets\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\SystemFiles\\"}

An alternative is external command approach which allows to restrict the depth of recursion.

According to this answer, starting from Windows Vista we have the ROBOCOPY utility as a part of the system which allows to list all subdirectories (including empty):

With[{dir = $UserBaseDirectory, depth = "2"}, 
 StringTrim@ImportString[
   RunProcess[{"robocopy", dir, dir, "/l", "/s", "/njh", "/njs", "/ns", "/lev:" <> depth},
     "StandardOutput"], "Lines", CharacterEncoding -> $SystemCharacterEncoding]]
{"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\_MyStyleSheets_\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\ApplicationData\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\Applications\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\Autoload\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\FrontEnd\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\Kernel\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\Knowledgebase\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\Licensing\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\Paclets\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\SearchIndices\\", 
"C:\\Users\\user\\AppData\\Roaming\\Mathematica\\SystemFiles\\"}
Alexey Popkov
  • 61,809
  • 7
  • 149
  • 368