22
$\begingroup$

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?

$\endgroup$
29
$\begingroup$

Maybe something like

Select[FileNames["*", "", Infinity], DirectoryQ]
$\endgroup$
  • 1
    $\begingroup$ 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.... $\endgroup$ – tkott Feb 27 '12 at 21:05
  • $\begingroup$ One may also use FileType instead of DirectoryQ. $\endgroup$ – Szabolcs Sep 15 '17 at 10:35
15
$\begingroup$

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]
$\endgroup$
  • $\begingroup$ Thanks Sjoerd. +1 for a detailed answer, but according to MMA.SE, Heike answered first (post 2389 vs. 2390) $\endgroup$ – tkott Feb 27 '12 at 21:06
  • 9
    $\begingroup$ @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. $\endgroup$ – Sjoerd C. de Vries Feb 27 '12 at 21:24
13
$\begingroup$

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}
$\endgroup$
  • 3
    $\begingroup$ I use ReadList often but had never discovered this. Good stuff! +1 $\endgroup$ – Andy Ross Feb 28 '12 at 6:26
  • 1
    $\begingroup$ @Andy thank you. I presume you mean the ability to read command line output? $\endgroup$ – Mr.Wizard Feb 28 '12 at 20:21
  • 2
    $\begingroup$ 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. $\endgroup$ – Andy Ross Feb 28 '12 at 20:25
  • 1
    $\begingroup$ @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). $\endgroup$ – Mr.Wizard Feb 28 '12 at 20:30
  • $\begingroup$ You should file that as a suggestion to WRI if you haven't already. $\endgroup$ – Andy Ross Feb 28 '12 at 20:35
0
$\begingroup$

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\\"}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.