In Emacs dired, how do I select files based on names from a list? - emacs

I open a buffer in emacs dired and select some files. How would I go about selecting files with the same name in another buffer? Ex: I select files a.txt, k.txt and z.txt in folder A. I then open a folder B in dired...how could I mark the files with the same names in folder B?

The following elisp code worked for me with buffers dired1 and dired2.
(progn
(switch-to-buffer "dired1")
(dlet ((files (mapcar (lambda (path) (car (last (split-string path "/"))))
(dired-get-marked-files))))
(switch-to-buffer "dired2")
(dired-mark-sexp '(member name files))))

Related

Emacs dired - using predefined variable

In emacs dired I want to do something I do quite often in Microsoft PowerShell.
In PowerShell, I have a set of folders that I always use, and I assign their full path to global variables in my profile script (similar to init.el in the emacs world) e.g:
$standardTemp = "C:\Long\Path\To\Folder"
If I am in another folder and I want to copy something to the above folder, I do:
copy myFile $standardTemp
Even more useful as a feature, is if I put a backslash after $standardTemp, it will expand it out, so I can go into subfolders if I need to. This is a very awesome feature and saves me a lot of time.
With the dired copy command can I do something similar, if I define variables with e.g. setq in my init.el file?
How about something like this?
;; Use ido
(require 'ido)
(ido-mode t)
;; Make a hash table to hold the paths
(setq my-target-dirs (make-hash-table :test 'equal))
;; Put some paths in the hash (sorry for Unix pathnames)
(puthash "home" "/home/jhrr/" my-target-dirs)
(puthash "target" "/home/jhrr/target/" my-target-dirs)
;; A function to return all the keys from a hash.
(defun get-keys-from-hash (hash)
(let ((keys ()))
(maphash (lambda (k v) (push k keys)) hash)
keys))
;; And the function to prompt for a directory by keyword that is looked
;; up in the hash-table and used to build the target path from the
;; value of the lookup.
(defun my-dired-expand-copy ()
(interactive)
(let* ((my-hash my-target-dirs)
(files (dired-get-marked-files))
(keys (get-keys-from-hash my-hash)))
(mapc (lambda (file)
(copy-file file
(concat
(gethash
(ido-completing-read
(concat "copy " file " to: ") keys) my-hash)
(file-name-nondirectory file))))
files)))
It's not exhaustively tested as I just whipped it up in 10 minutes, but it does the job and it can handle multiple files.
You will need to open the dired buffer in the directory the files are in and mark each file you want to copy with 'm', then invoke my-dired-expand-copy and it will prompt you for a target destination (in the form of a keyword from the hash-table we set-up) for the file before, finally, copying the file over to the directory that maps to the target keyword.
It doesn't quite cover the sub-directories use-case you mention, but it shouldn't be too hard to get there given a bit more hacking.
UPDATE:
This should now prompt you to be able to descend into subdirectories from an original target; maybe not the most mind-shatteringly wonderful UX on the whole, but, it works:
(defun my-dired-expand-copy-2 ()
(interactive)
(let* ((my-hash my-target-dirs)
(files (dired-get-marked-files))
(keys (get-keys-from-hash my-hash)))
(mapc (lambda (file)
(let ((target (gethash
(ido-completing-read
(concat "copy " file " to: ") keys) my-hash)))
(if (y-or-n-p "Descend?")
;; Descend into subdirectories relative to target dir
(let ((new-target (ido-read-directory-name "new dir: " target)))
(copy-file file (concat new-target
(file-name-nondirectory file)))
(message (concat "File: " file " was copied to " new-target)))
;; Else copy to root of originally selected directory
(copy-file file (concat target (file-name-nondirectory file)))
(message (concat "File: " file " was copied to " target)))))
files)))
When I need to use dired to get to frequently-used directories, I use the standard emacs bookmarking capabilities.
I manually navigate to the directory, and press
C-x r m
to execute the command
bookmark-set
You'll be prompted for a name for the bookmark. Enter a shortcut that you can remember.
At this point, anytime you want to open that directory within dired, simply execute the command
bookmark-jump
with the keys
C-x r b
Enter your shortcut to the directory, and dired will open to that location.
To copy from one directory to another, ensure you have the following set in your init file
(setq dired-dwim-target t)
Then you can open a dired window for the source directory, and another window for the target directory within in the same frame, and dired will automatically assign the source and target location to the appropriate directories.
Note this is just a subset of what emacs bookmarks can do for you!
Chris
In addition to using bookmarks, consider using directory-name aliases (e.g. symlinks) or directory-abbrev-alist. See the Emacs manual, node File Aliases.
If you want to insert the value of an environment variable into the minibuffer, you can do it this way:
C-u M-: (getenv "THE-VARIABLE")
where THE-VARIABLE is the variable name. Using C-u inserts the value of evaluating the sexp into the current buffer (in this case the minibuffer).
So you would, say, use C to copy the marked files in Dired, and then use C-u with a getenv sexp for the existing variable you have, to insert its value into the minibuffer when prompted for the directory to copy to.
(Depending on your Emacs setup, you might need to set enable-recursive-minibuffers to non-nil, to be able to use M-: from the minibuffer.)

in Emacs, how to maintain a list of recent directories?

In Emacs, I use recentf extensively. Rather than calling find-files, I usually call a custom function xsteve-ido-choose-from-recentf instead which allows me to choose from my recentf files.
How do I create and maintain a separate list of recent directories, separate from the list of recent files? So that instead of calling dired, I could call something like ido-choose-from-recent-directories?
You ask, in your comment replying to #Stefan's answer: And how do I get from the above to viewing a list of recent directories? -
The answer is that you use the little-known fact that if the DIRNAME argument to dired is a list of (a) the new Dired buffer name followed by (b) file (or directory) names, then Dired is opened for just those files/dirs. IOW:
M-: (dired (cons DIRED-BUFNAME YOUR-LIST-OF-RECENT-DIRECTORIES))
For example:
M-: (dired '("My Dired Buffer" "/a/recent/dir/" "/another/recent1/" "/another/"))
If you use library Dired+ then you can provide such a list interactively, by using a non-positive prefix arg with dired.
But in this case you want to write a command that first gathers the list of recent directories and then opens Dired for them. This should do it:
(defun dired-recent (buffer)
"Open Dired in BUFFER, showing the recently used directories."
(interactive "BDired buffer name: ")
(let ((dirs (delete-dups
(mapcar (lambda (f/d)
(if (file-directory-p f/d)
f/d
(file-name-directory f/d)))
recentf-list))))
(dired (cons (generate-new-buffer-name buffer) dirs))))
That works for me. However, vanilla Emacs does not let you use i to insert the listing for any directory that is not in the same directory tree as the default-directory of the Dired buffer. That means that the above code will work OK, but you will not be able to insert any of the listed directories.
To be able to do that, load library dired+.el. Dired+ also fixes a couple of other inadequacies wrt the vanilla handling of a cons arg to dired.
The above code, together with Dired+ should give you what you want.
UPDATE
I just added this to Dired+. These are the commands added: diredp-dired-recent-dirs and diredp-dired-recent-dirs-other-window.
UPDATE 2
I made it simple to choose which of the recently used directories to include or exclude. Use a prefix arg to initiate such choosing. With no prefix arg you get all recent dirs. I also made it possible to use a prefix arg to be prompted for the ls switches. Here is the doc s tring of diredp-dired-recent-dirs:
Open Dired in BUFFER, showing recently used directories.
You are prompted for BUFFER.
No prefix arg or a plain prefix arg (`C-u', `C-u C-u', etc.) means
list all of the recently used directories.
With a prefix arg:
* If 0, `-', or plain (`C-u') then you are prompted for the `ls'
switches to use.
* If not plain (`C-u') then:
* If >= 0 then the directories to include are read, one by one.
* If < 0 then the directories to exclude are read, one by one.
When entering directories to include or exclude, use `C-g' to end.
Finally, I added bindings for the commands: C-x R (same window) and C-x 4 R (other window), where R is Shift + r.
You don't need to maintain a separate list (which would be a lot of work). Instead, you can extract that list from the recentf list. E.g.
(delete-dups
(mapcar (lambda (file)
(if (file-directory-p file) file (file-name-directory file)))
recentf-list))
Pragmatic Emacs found the solution.
Here is a function to give you a list of recent directories, using ivy
(part of swiper) to narrow it dynamically, and then open the selected
one in dired.
;; open recent directory, requires ivy (part of swiper)
;; borrows from http://stackoverflow.com/questions/23328037/in-emacs-how-to-maintain-a-list-of-recent-directories
(defun bjm/ivy-dired-recent-dirs ()
"Present a list of recently used directories and open the selected one in dired"
(interactive)
(let ((recent-dirs
(delete-dups
(mapcar (lambda (file)
(if (file-directory-p file) file (file-name-directory file)))
recentf-list))))
(let ((dir (ivy-read "Directory: "
recent-dirs
:re-builder #'ivy--regex
:sort nil
:initial-input nil)))
(dired dir))))
(global-set-key (kbd "C-x C-d") 'bjm/ivy-dired-recent-dirs)
Source:
Open a recent directory in dired: revisited | Pragmatic Emacs

Concatenating multiple files in emacs

Is there a fast (automatic) way to create one long file from all of the files in a directory using emacs? IE
>Text_1.txt
>{contents of Text_1}
>Text_2.txt
>{contents of text2}
>FinalResult.txt
>{contents of Text_1
>contents of Text2}
How about this:
(defun insert-my-files ()
(interactive)
(let ((dir (read-directory-name "Directory to insert: ")))
(mapc #'(lambda (file)
(let ((file-full (concat dir file)))
(insert-file-contents file-full)))
(cddr (directory-files dir)))))
Call it with M-x insert-my-files, and it will insert the contents of the directory you supply.
I don't know if you'd call it a fast way, but insert-file can be used to insert a file into an existing buffer.
For the specific case you're talking about though, the fastest way is probably from the command line: cat * > FinalResult.txt

Recursively adding .org files in a top-level directory for org-agenda-files takes a long time

I'm trying to find a way to quickly recurse through every subdirectory searching for org files. I've found several solutions (Elisp Cookbook, and several solutions on github), but they don't handle my real world usage (hundreds of directories (and subdirectories) and hundreds of org files). They seem to run forever on my system (Windows 7, with max-lisp-eval-depth = 10000). My work around is to add each directory manually to my org-agenda-list, but it's annoying and I know I've probably forgotten some. Any suggestions would be appreciated.
Haven't seen other people post this, so I will do.
Have you tried load "find-list" library and use its "find-lisp-find-files" function?
I added these lines in my org config and it works, but it may not fit your performance requirement:
(load-library "find-lisp")
(setq org-agenda-files
(find-lisp-find-files "FOLDERNAME" "\.org$"))
source: http://emacs-orgmode.gnu.narkive.com/n5bQRs5t/o-multiple-recursive-directories-with-org-agenda-files
The following code works well in emacs 24.3+:
;; Collect all .org from my Org directory and subdirs
(setq org-agenda-file-regexp "\\`[^.].*\\.org\\'") ; default value
(defun load-org-agenda-files-recursively (dir) "Find all directories in DIR."
(unless (file-directory-p dir) (error "Not a directory `%s'" dir))
(unless (equal (directory-files dir nil org-agenda-file-regexp t) nil)
(add-to-list 'org-agenda-files dir)
)
(dolist (file (directory-files dir nil nil t))
(unless (member file '("." ".."))
(let ((file (concat dir file "/")))
(when (file-directory-p file)
(load-org-agenda-files-recursively file)
)
)
)
)
)
(load-org-agenda-files-recursively "/path/to/your/org/dir/" ) ; trailing slash required
It does not require intermediate files creation and you can put it on a shortcut as well.
To be able to refile to any file found add this:
(setq org-refile-targets
'((nil :maxlevel . 3)
(org-agenda-files :maxlevel . 1)))
How about storing the list of org-agenda directories in a file that you (automatically) update every once in a while, when you know the direcory structure has changed and you have some time.
You could for example use something like this:
;; From http://www.emacswiki.org/emacs/ElispCookbook#toc58
(defun directory-dirs (dir)
"Find all directories in DIR."
(unless (file-directory-p dir)
(error "Not a directory `%s'" dir))
(let ((dir (directory-file-name dir))
(dirs '())
(files (directory-files dir nil nil t)))
(dolist (file files)
(unless (member file '("." ".."))
(let ((file (concat dir "/" file)))
(when (file-directory-p file)
(setq dirs (append (cons file
(directory-dirs file))
dirs))))))
dirs))
(setq my-org-agenda-root "~/org")
(setq my-org-agenda-files-list "~/.emacs.d/org-agenda-list.el")
(defun my-update-org-agenda-files ()
"Create or update the `my-org-agenda-files-list' file.
This file contains elisp code to set `org-agenda-files' to a
recursive list of all children under `my-org-agenda-root'. "
(interactive)
(with-temp-buffer
(insert
";; Warning: this file has been automatically generated\n"
";; by `my-update-org-agenda-files'\n")
(let ((dir-list (directory-dirs my-org-agenda-root))
(print-level nil)
(print-length nil))
(cl-prettyprint `(setq org-agenda-files (quote ,dir-list))))
(write-file my-org-agenda-files-list)))
(load my-org-agenda-files-list)
Every once in a while, run M-xmy-update-org-agenda-files to update the list.
As of Emacs 25, you can use directory-files-recursively which returns all files matching a regex from a root directory.
(defun org-get-agenda-files-recursively (dir)
"Get org agenda files from root DIR."
(directory-files-recursively dir "\.org$"))
(defun org-set-agenda-files-recursively (dir)
"Set org-agenda files from root DIR."
(setq org-agenda-files
(org-get-agenda-files-recursively dir)))
(defun org-add-agenda-files-recursively (dir)
"Add org-agenda files from root DIR."
(nconc org-agenda-files
(org-get-agenda-files-recursively dir)))
(setq org-agenda-files nil) ; zero out for testing
(org-set-agenda-files-recursively "~/Github") ; test set
(org-add-agenda-files-recursively "~/Dropbox") ; test add
You can view the contents of org-agenda-files by typing C-hv org-agenda-files.
Little late to the party, and probably not the answer you were hoping for, but the only way I found to speed up my agenda load times was by not including some directories which had thousands of org files.
(setq org-directory "~/org/"
my-agenda-dirs '("personal" "projects" "todos" "work")
org-agenda-files (mapcan (lambda (x) (directory-files-recursively
(expand-file-name x org-directory)
"\.org$"))
my-agenda-dirs))
Essentially rather than recursing over my entire org dir I only recurse through a select few of it's subdirs.
I also tried "recursive directory listing" at Emacs startup. It simply is way to loooong to be usable. So, try to stick with a limited number of "root" directories where you put your agenda files.
Even better is sticking with "~/org", and possibly a few subdirs like "work" and "personal", and that's it. You can put there your agenda files, and have links Inside them to your real project root dirs.
I know, this is not optimal, but I don't have anything better right now.

How to make all org-files under a folder added in agenda-list automatically?

I am using org-mode to write notes and org-agenda to organize all notes, especially to search some info. by keyword or tag.
C-c a m can search some files by tag inputed, C-c a s by keyword ,those functions from org-agenda are well to utilize, however, I need to add org-file into the agenda-list by hand.
I added some codes into .emacs, such as
(setq org-agenda-files (list "path/folder/*.org"))
or
(setq org-agenda-files (file-expand-wildcards "path/folder/*.org"))
but, both failed to add files under the folder specified into agenda-list automatically, so I can't search keyword or tag among those org-files, unless that I open a org-file and type C-c [ to add it into agenda-list.
How can I make all org-files under a folder automatically added in agenda?
Just naming the directory should be enough. For example this works for me very well:
(setq org-agenda-files '("~/org"))
Also take a look at org-agenda-text-search-extra-files; it lets you
add extra files included only in text searches. A typical value might
be,
(setq org-agenda-text-search-extra-files
'(agenda-archives
"~/org/subdir/textfile1.txt"
"~/org/subdir/textfile1.txt"))
Caveat: If you add a file to the directory after you have started
Emacs, it will not be included.
Edit: (2018) To include all files with a certain extension in the extra files list you can try the following function I wrote sometime back (a more recent version might be available here).
;; recursively find .org files in provided directory
;; modified from an Emacs Lisp Intro example
(defun sa-find-org-file-recursively (&optional directory filext)
"Return .org and .org_archive files recursively from DIRECTORY.
If FILEXT is provided, return files with extension FILEXT instead."
(interactive "DDirectory: ")
(let* (org-file-list
(case-fold-search t) ; filesystems are case sensitive
(file-name-regex "^[^.#].*") ; exclude dot, autosave, and backupfiles
(filext (or filext "org$\\\|org_archive"))
(fileregex (format "%s\\.\\(%s$\\)" file-name-regex filext))
(cur-dir-list (directory-files directory t file-name-regex)))
;; loop over directory listing
(dolist (file-or-dir cur-dir-list org-file-list) ; returns org-file-list
(cond
((file-regular-p file-or-dir) ; regular files
(if (string-match fileregex file-or-dir) ; org files
(add-to-list 'org-file-list file-or-dir)))
((file-directory-p file-or-dir)
(dolist (org-file (sa-find-org-file-recursively file-or-dir filext)
org-file-list) ; add files found to result
(add-to-list 'org-file-list org-file)))))))
You can use it like this:
(setq org-agenda-text-search-extra-files
(append (sa-find-org-file-recursively "~/org/dir1/" "txt")
(sa-find-org-file-recursively "~/org/dir2/" "tex")))
Edit: (2019) As mentioned in the answer by #mingwei-zhang and the comment by #xiaobing, find-lisp-find-files from find-lisp and directory-files-recursively also provides this functionality. However, please note in these cases the file name argument is a (greedy) regex. So something like (directory-files-recursively "~/my-dir" "org") will give you all Org files including backup files (*.org~). To include only *.org files, you may use (directory-files-recursively "~/my-dir" "org$").
There is a simpler way of doing recursive search of org files (courtesy #xiaobing):
(setq org-agenda-files (directory-files-recursively "~/org/" "\\.org$"))
EDIT: You can also filter out certain directory from lookup by adding a array filter. Example, filtering out all org files in xxxx/xxx/daily/ directory:
(setq org-agenda-files
(seq-filter (lambda(x) (not (string-match "/daily/"(file-name-directory x))))
(directory-files-recursively "~/Notes/roam" "\\.org$")
))
For Emacs <25, you can use find-lisp-find-files:
(load-library "find-lisp")
(setq org-agenda-files
(find-lisp-find-files "FOLDERNAME" "\.org$"))