Is it possible to use wildcards such as * in elisp to expand filenames?
For example, I saw that
(expand-file-name "~/.emacs.d/elpa/babel-repl-20160504.1501")
expands to /home/username/.emacs.d/elpa/babel-repl-20160504.1501.
Is there a function to also expand filenames containing wildcards so that we can do:
(expand-file-name "~/.emacs.d/elpa/babel-repl*")
(taking the first filename if multiple matches exist)?
I tried the above, but expand-file-name does not seem to recognize * (Emacs 24.5 from Ubuntu 16.04)
-- EDIT --
#Drew I tried (byte-recompile-directory (file-expand-wildcards "~/.emacs.d/elpa/babel-repl*/") 0) but got an error Wrong type argument: stringp, nil.
I am not very familiar with elisp and the stringp type (a list of strings, I guess). I tried to use a (car ) on the returned value of file-expand-wildcards in order to get the first matched filename. But Emacs still work start correctly.
Any pointers?
Try file-expand-wildcards:
file-expand-wildcards is a compiled Lisp function in files.el.
(file-expand-wildcards PATTERN &optional FULL)
Expand wildcard pattern PATTERN.
This returns a list of file names which match the pattern.
If PATTERN is written as an absolute file name,
the values are absolute also.
If PATTERN is written as a relative file name, it is interpreted
relative to the current default directory, default-directory.
The file names returned are normally also relative to the current
default directory. However, if FULL is non-nil, they are absolute.
That gives you a list of expansions. Pick the first one or any one you want.
Related
If a directory contains many files and I want to go to files(or subdirectories) with name starting with a letter (or a string), is there any good way to do it? I know in Far File Manager people can press Alt and start typing the name and the cursor will move as you type, I am wondering if Emacs has something similar.
The fastest way to answer this question is copying a section from the emacs info files.
To get to this text type C-h i d m emacs ret and then isearch for an appropriate substring ignoring the first Failing I-search or just go to info with C-h i and then directly go to the info node mentioned below with g and then typing the node name followed by ret.
The first from the info-node (emacs) Dired and Find works whether one is in a dired buffer or not:
To search for files with names matching a wildcard pattern use `M-x
find-name-dired'. It reads arguments DIRECTORY and PATTERN, and
chooses all the files in DIRECTORY or its subdirectories whose
individual names match PATTERN.
The second from the info-node (emacs) Dired Navigation works in dired buffers but only applies to the currently listed files. But, note that you can list subdirectories with i before.
`M-s f C-s' (`dired-isearch-filenames') performs a forward
incremental search in the Dired buffer, looking for matches only
amongst the file names and ignoring the rest of the text in the buffer.
`M-s f M-C-s' (`dired-isearch-filenames-regexp') does the same, using a
regular expression search. If you change the variable
`dired-isearch-filenames' to `t', then the usual search commands also
limit themselves to the file names; for instance, `C-s' behaves like
`M-s f C-s'. If the value is `dwim', then search commands match the
file names only when point was on a file name initially. *Note
Search::, for information about incremental search.
EDIT:
To isearch a directory recursively you may first list it recursively.
You can list sub-directories recursively by calling dired with prefix-arg and adding R to the list of switches.
The following snippet for the emacs initialization file would even simplify this task:
(defun dired-noselect-maybe-recursive (dir-or-list &optional switches)
"Like `dired-noselect' but lists sub-directories when last character is ?/."
(if (and (null switches)
(= (aref dir-or-list (1- (length dir-or-list))) ?/))
(dired-noselect dir-or-list "-alR")
(dired-noselect dir-or-list switches)
))
(setq find-directory-functions (subst 'dired-noselect-maybe-recursive 'dired-noselect find-directory-functions))
With this snippet you get a normal listing of directories if you call find-file (C-x C-f) for a directory without a slash at the end and you get a recursive listing if you call it with a slash at the end.
But be careful. Recursive directory listing can take its time. If you get nervous you can always quit with C-g.
isearch will do that. control-s, then start typing.
Since it's emacs' general-purpose search function, it could start out matching other things in the buffer until you get enough of the name in.
I just want to add a trick that I like to use that's
somewhat related to your question:
(add-hook
'dired-mode-hook
(lambda()
(define-key dired-mode-map "j" 'ido-find-file)))
Obviously it works for just one file, and it opens it, instead
of offering a multitude of things that dired can do to a file,
but at least it's fast.
#Tobias and #jl8e have both answered your question directly: use C-s (incremental search). Nothing to add to that.
But if you are going to be using a subset of the listed files multiple times, or you are going to perform the same operation on a subset of the files, and if that subset can be identified by a file-name pattern (e.g. same prefix), then you can use %m to mark all files matching a regexp. Once marked, you can do all kinds of things on the set of files marked (or on the set of those unmarked).
(One of the things you can do is just omit the marked or unmarked file names from the listing, and hence from operations.)
Dired is really mostly about examining and operating on sets of files. The singleton set is a special case, and all operations (keys) that operate on the marked files also operate on the current line's file only, if none are marked.
Often, I know the file name but don't remember or find it cumbersome to specify the exactly directory. I'd like to be able to do:
find-file x/y/*/some_file.txt
where * would allow searching in recursive directories instead of just the current one.
As described here:
M-x find-name-dired
After you provide the directory and the pattern the matching files will be displayed in a Dired buffer. You can navigate the file list (C-n, C-p, etc.) and open files as you wish (Ret).
I've long looked for that feature and I'm now satisfied with what I found: I'm using helm-projectile.
Projectile is a project interaction library. Projects are VCS directories or directories containing a .projectile file. It is based on GNU find (but offers a pure emacs lisp implementation too)
https://github.com/bbatsov/projectile
(you can install it with packages.el). You can use it alone: call projectile-find-file (or projectile-mode and C-c p f). It uses ido for the completion by default, but in that case I prefer the interface of helm-projectile.
My colleagues kept telling me to use Sublime Text because of that feature. Fortunately, I found projectile :)
ido-find-file does this: when the filename that you enter doesn't match
anything in the current directory, it uses something like locate to match
what you wrote against all files on your system.
The setup is just one line:
(ido-mode)
After this, C-x C-f will call ido-find-file.
Also, I usually add:
(setq ido-enable-flex-matching t)
because I like it, but with it you will locate more results,
i.e. foo will also match froo.
In case of files in a project directory, I have used Fiplr that uses a fuzzy search library by the same author.
https://github.com/d11wtq/fiplr
This is available from MELPA.
Other solution that I haven't tried is https://github.com/technomancy/find-file-in-project
edit: I found much better with projectile, see my other answer.
There is find-recursive.el : http://www.webweavertech.com/ovidiu/emacs/find-recursive.txt
Download it, put it in your load path and require it in your .emacs: (it isn't available through ELPA or el-get)
(require 'find-recursive)
now when you call M-x find-recursive, it will ask for a file name to search for recursively, a base directory, then you have to select one file among a result list. It isn't integrated into ido unfortunately, but I like it, it is useful sometimes.
Here is a solution that gives you selectivity over the files and subdirectories searched. To use it you need both Dired+ and Icicles.
The command, used only in Dired, is icicle-visit-marked-file-of-content-recursive (or the -other-window version of it). It is bound to M-+ C-F (for other window, use M-+ C-O -- and yes, those are uppercase F and O).
What it does:
It provides, as candidates for completion, all of the files marked in the current Dired directory, pslus all of those in any marked subdirectories that have Dired buffers, and so on recursively.
You can visit any number of such candidates that match your minibuffer input, including all of them. You can visit some that match a minibuffer input pattern, then change the pattern to match and visit others --- all in the same command invocation.
"Matching" your input can mean either or both:
Matching the file name
Matching the file content
That is, your input can be two-part (separated by hitting C-M-j: a file-name part and a file-content part. Narrow the choices by matching file names, and narrow further by matching text in those files. This search is fast -- it succeeds as soon as it finds a single match in the file.
You can omit either part: match only file names (fast) or only file contents (slower, naturally).
You can combine input patterns -- progressive completion. It is much easier to add additional patterns to match, incrementally and interactively, than it is to try to come up with a single regexp that does just what you want.
Each input pattern (each part of a two-part pattern) can be a regexp. Fuzzy matching is also available.
You can alternatively just insert subdirectories in your Dired buffer, instead of having them as separate Dired buffers. Any files and subdirs marked in a Dired buffer are handled the same way.
If you do not want to descend into subdirs that are marked, you can use the non-recursive version of the command, icicle-visit-marked-file-of-content, bound to C-F (C-O for other-window).
If you also use Bookmark+ then you can bookmark a Dired buffer or a set of Dired buffers. The bookmark records which files and subdirectories are marked, which subdirectories are inserted, and which files and subdirectories are omitted. Jumping to such a bookmark restores all of these things. This gives you a way to snapshot a project, which you can then search using M-+ C-F etc.
A lot of options listed, I'm using next command:
find-file-in-current-directory
You can put it in your .emacs like this:
;; Find file in current directory:
(global-set-key (kbd "C-M-,") 'find-file-in-current-directory)
Interface looks very familiar to those who use fsf tools (https://github.com/junegunn/fzf)
If you have helm, you can use this:
(defun my/helm-find-file-recursively ()
"Recursively find files in glob manner, in the specified directory."
(interactive)
(helm-find 'ask-for-dir))
(global-set-key (kbd "C-c o f") 'my/helm-find-file-recursively)
Icicles command icicle-locate (or icicle-locate-file, if you do not have a locate program for your OS) is made for that. Locate a file anywhere under a given directory (including ~HOME or the root directory).
You can match against any parts of the absolute file name (i.e., the path and the file name). You can use several kinds of matching, including regexp, substring, and fuzzy matching.
For a more programmatic approach (using the built-in files library):
(directory-files-recursively "~/assignments/" "[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.org")
;; ("~/assignments/a/b/2022-05-18.org"
;; "~/assignments/c/d/2022-07-15.org"
;; "~/assignments/e/f/2022-08-08.org")
Here's a thin wrapper around that function which reads a filename from the minibuffer and returns a list of matching directories (starting from the current directory), from which you select an entry which will then be visited:
(defun my/find-directory ()
"Find the directory containing FILE."
(interactive)
(let ((matches (directory-files-recursively default-directory (read-from-minibuffer "Filename: "))))
(if matches
(find-file
(completing-read
"Pick a directory: "
(mapcar 'file-name-directory matches))))
(message "No matching directories.")))
I'm running a command with compile so I can link from error messages to the associated source, but I need to transform a chunk of the content of matched lines to get the file to link to. (The line shows a clojure namespace, like foo-bar.quux, which needs to be transformed into foo_bar/quux.clj.)
The documentation of compilation-error-regexp-alist says, in part,
Each elt has the form (REGEXP FILE [LINE COLUMN TYPE HYPERLINK HIGHLIGHT...]). ... FILE can also have the form (FILE FORMAT...), where the FORMATs (e.g. \"%s.c\") will be applied in turn to the recognized file
name, until a file of that name is found. Or FILE can also be a function that returns (FILENAME) or (RELATIVE-FILENAME . DIRNAME). In the former case, FILENAME may be relative or absolute.
When I add entries to compilation-error-regexp-alist-alist and compilation-error-regexp-alist with a function in FILE position, my function is called with no arguments. How do I get the matched line inside that function?
Opening compile.el and then searching with C-s (funcall jumped me to:
(setq file (if (functionp file) (funcall file)
(match-string-no-properties file))))
which seems to be the relevant spot and shows that the functions is indeed called with no arguments and that the match-data is still very much valid, so you can extract the file name with (match-string <the-file-sub-group>).
I am manually constructing path strings in Elisp by concatenating partial paths and directory names. Unfortunately sometimes the paths end with slash, sometimes not. Therefore, I need to insert slash before concatenating a directory name when necessary but not otherwise. What's a good way to do this?
(file-name-as-directory dir) will return directory path dir with a trailing slash, adding one if necessary, and not otherwise.
If you had your sequence of partial paths in a list, you could do something like:
(let ((directory-list '("/foo" "bar/" "p/q/" "x/y"))
(file-name "some_file.el"))
(concat
(mapconcat 'file-name-as-directory directory-list "")
file-name))
"/foo/bar/p/q/x/y/some_file.el"
or as an alternative, if you wanted to include the file name in the list, you could utilise directory-file-name which does the opposite of file-name-as-directory:
(let ((path-list '("/foo" "bar/" "p/q/" "x/y/some_file.el")))
(mapconcat 'directory-file-name path-list "/"))
"/foo/bar/p/q/x/y/some_file.el"
(Someone please correct me if using directory-file-name on a non-directory is not portable?)
The easiest way to assemble file names from parts of questionable content is with expand-file-name. For example:
(expand-file-name "foo.txt")
this common form will give you a full file name based on default-directory:
/home/me/foo.txt
but if you have a variable 'dir' whose content is "/home/them/subdir" and want to use that, do this:
(expand-file-name "foo.txt" dir)
it doesn't matter if dir ends in / or not. If you are on some other platform, and contains the other slash, it will do the right thing then too. Do you have a mix? Just stack them:
(expand-file-name "foo.txt" (expand-file-name "somesubdir" dir))
Something like this should work as a starting point, although you'd want to flesh it out a bit to make it platform independent, etc.
(defun append-path-component (path new-part)
(if (string-match ".*/$" path)
(concat path new-part)
(concat path "/" new-part)))
As per usual, there's probably some bit of elisp that already does this that I'm just not aware of.
Unless you really care about keeping relative file names as relative, then it's always much better to avoid concat and use expand-file-name instead.
(defun* tofilename (directorylist &optional (filename nil))
"concatenate directory names into a path, with an optional file name as last part"
(concat
(mapconcat 'directory-file-name directorylist "/")
"/"
filename))
(tofilename '("~/" "Temp/") "temp.txt")
;; => "~/Temp/temp.txt"
(tofilename '("~/" "Temp/"))
;; => "~/Temp/"
(tofilename '("~/" "Temp/" "test"))
;; => "~/Temp/temp/"
If you deal with file manipulation, joining and splitting filepaths, checking empty directories and such, I strongly recommend installing f.el, modern file manipulation library. You will have a huge set of file and filepath manipulation functions under one namespace and will never reinvent the wheel again.
The function you need is f-join, it concatenates parts of a path, adding slash only where needed.
Are there any library or function that performs a bash-like glob expansion for emacs lisp?
For example:
(directory-files-glob "~/Desktop/*")
> ("/home/user/Desktop/file1" "/home/user/Desktop/file2")
If there isn't such a function are there any hint/suggestion on how to implement it?
EDIT:
I've found in the docs also an useful function that does quite exactly this:
file-expand-wildcards:
This function expands the wildcard pattern pattern, returning a list of file names that match it.
Not sure why this was overlooked maybe it was not in emacs in 2010 but in a current emacs at least there is function file-expand-wildcards
(file-expand-wildcards "~/Desktop/*")
which does exactly what you want to do..
Check out the documentation for directory-files:
(directory-files "~/Desktop" nil ".")
Note: The third argument is a regular expression - not globbing.
It is straight forward to turn globbing patterns into regular expressions. eshell comes with a translation package which you can use:
(require 'em-glob)
(defun directory-files-glob (path)
(directory-files (file-name-directory path)
nil
(eshell-glob-regexp (file-name-nondirectory path))))
And, if you want full exposure to eshell's globbing (with directories), there's probably a way to get that. The above assumes that the globbing part is in the non-directory portion of the path.
Package f adds a huge amount of file and filepath manipulation functions under a consistent naming scheme. It has a function f-glob that does exactly that:
(f-glob "~/doc/*.org") ; returns a list of files ending with ".org"
(f-glob "*.org" "~/doc/") ; they all behave the same
(f-glob "*.org" "~/doc")