28
$\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$
0

4 Answers 4

33
$\begingroup$

Maybe something like

Select[FileNames["*", "", Infinity], DirectoryQ]
$\endgroup$
2
  • 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, 2012 at 21:05
  • $\begingroup$ One may also use FileType instead of DirectoryQ. $\endgroup$
    – Szabolcs
    Sep 15, 2017 at 10:35
17
$\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$
2
  • $\begingroup$ Thanks Sjoerd. +1 for a detailed answer, but according to MMA.SE, Heike answered first (post 2389 vs. 2390) $\endgroup$
    – tkott
    Feb 27, 2012 at 21:06
  • 10
    $\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$ Feb 27, 2012 at 21:24
14
$\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$
5
  • 3
    $\begingroup$ I use ReadList often but had never discovered this. Good stuff! +1 $\endgroup$
    – Andy Ross
    Feb 28, 2012 at 6:26
  • 1
    $\begingroup$ @Andy thank you. I presume you mean the ability to read command line output? $\endgroup$
    – Mr.Wizard
    Feb 28, 2012 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, 2012 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, 2012 at 20:30
  • $\begingroup$ You should file that as a suggestion to WRI if you haven't already. $\endgroup$
    – Andy Ross
    Feb 28, 2012 at 20:35
1
$\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 and acknowledge that you have read and understand our privacy policy and code of conduct.

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