2

Is it possible from within Emacs to find directories with a given depth from the current directory from within Emacs and receive their respective location in form of a list? I want it to work cross platform...

sidharth arya
  • 235
  • 1
  • 7

4 Answers4

1

I'd be inclined to use find for that:

(let ((dirname (expand-file-name "~/"))
      (depth 3))
  (split-string
   (shell-command-to-string
    (format "find %s -mindepth %d -maxdepth %d -type d -print0 2>/dev/null"
            (shell-quote-argument dirname) depth depth))
   "\0" :omit-nulls))

n.b. -mindepth, -maxdepth, and print0 are not POSIX options, so YMMV regarding portability.

phils
  • 48,657
  • 3
  • 76
  • 115
  • I know but find would not work across different platforms. – sidharth arya Oct 23 '18 at 09:54
  • @sidhartharya You didn't mention the requirement that the code should work across platforms in your question. Therefore, this answer is equally valid as the others. (That's why I up-voted it.) Furthermore, the gnu-`find` tool is available for several platforms. E.g., on [Windows](https://www.microsoft.com/en-us/windows) by [mingw](http://www.mingw.org/wiki/MSYS). – Tobias Oct 24 '18 at 09:43
  • Yes gnu find is available across platforms but I want to do it , in a way, so the user doesn't have to explicitely install something specifically for that.. And also if i had to issue a shell command why would i ask it here. But thank's i will make the edit – sidharth arya Oct 24 '18 at 09:51
1

The elgrep-search command has options :mindepth and :maxdepth. Elgrep is available via package-install from the melpa package archive (instructions how to add melpa to package-archives can be found on the melpa "Getting Started" page).

You can use elgrep as follows to get a list of matching file names of a specific depth. In the example I search in my HOME-directory (~/) for files matching \.el\' without in-file matches (that's the nil) with minimal and maximal recursion depth 2.

(require 'elgrep)
(mapcar 'car (elgrep-search "~/" "\\.el\\'" nil :recursive t :mindepth 2 :maxdepth 2))

Elgrep actually returns filematches. That are conses of the file name and a list of in-file matches. In our case the list of in-file matches is empty since we passed nil for the in-file regexp. Because of that structure you need the (mapcar 'car ...). See the documentation of the elgrep-search command for further details.

Note, that mindepth and maxdepth are also available via the gui-version elgrep-menu. There you can let elgrep search asynchronously. That way it does not block your Emacs.

Tobias
  • 32,569
  • 1
  • 34
  • 75
1

Library find-dired+.el can help with this. C-h f find-dired:

find-dired is an interactive compiled Lisp function in find-dired+.el.

(find-dired DIR ARGS &optional DEPTH-LIMITS EXCLUDED-PATHS)

Run find and put its output in a buffer in Dired Mode.

Then run find-dired-hook and dired-after-readin-hook.

The find’ command run (after changing intoDIR) is essentially this, whereLS-SWITCHESis(car find-ls-option)`:

 find . \( ARGS \) LS-SWITCHES

Optional args:

  • DEPTH-LIMITS: Minimum and maximum depths: (MIN-DEPTH MAX-DEPTH).
  • EXCLUDED-PATHS: Strings matching paths to be excluded. Uses find switch -path.

When both optional args are non-nil, the find command run is this:

find . -mindepth MIN-DEPTH -maxdepth MAX-DEPTH
     \( -path EXCLUDE1 -o -path EXCLUDE2 ... \)
     -prune -o \( ARGS \) LS-SWITCHES

where EXCLUDE1, EXCLUDE2... are the EXCLUDED-PATHS, but shell-quoted.

Same thing for the related commands: find-name-dired, find-grep-dired, and find-time-dired - they all take arg DEPTH-LIMITS.


To reply to @Tobias's comment: Yes, find-dired+.el (which extends standard library find-dired.el) depends on a UNIX or GNU/Linux find command. On MS Windows you can use Cygwin or any other porting of the command to Windows.

Drew
  • 75,699
  • 9
  • 109
  • 225
  • As I understand the code [`find-dired+.el`](https://www.emacswiki.org/emacs/download/find-dired%2b.el) uses the external `find` program. Therefore, the same comments as for [Phils answer](https://emacs.stackexchange.com/a/45521/2370) apply. Disclaimer: I had only a short glance on the code. It may be that you have some fallback there that uses emacs-internals when gnu-`find` is not available. I didn't check for that. Please correct me if that is the case. – Tobias Oct 24 '18 at 09:46
1

You can use the ls command of the built-in eshell for listing the files with some predefined depth.

You have to specify the depth by the right number of concatenated file name generator expressions */.

Example:

(split-string
 (substring-no-properties
  (eshell-command-result "ls /usr/share/emacs/26.1/*/*/*/*/*/*(.)")
  )
 "\n" t)

The appended modifier (.) says that the generator only expands regular file paths.

Note that the backend eshell-do-ls command is a pure Emacs Lisp implementation of the ls command. Yet in this case the actual work is not done by ls but by eshell-glob.

If you also want to list directories you should add the -d switch to the ls command. Otherwise the files contained in the directories would also be listed:

(split-string
 (substring-no-properties
  (eshell-command-result "ls -d /usr/share/emacs/26.1/*/*/*/*/*/*")
  )
 "\n" t)

If you actually don't want any special features of ls you can also use pure eshell-extended-glob:

(eshell-extended-glob "/usr/share/emacs/26.1/*/*/*/*/*/*")

If you want to list all files with some minimal depths you can expand with the two-star form **, e.g.,

(eshell-extended-glob "/usr/share/emacs/26.1/*/*/*/**/*")
Tobias
  • 32,569
  • 1
  • 34
  • 75