How to write a function for finding all files in a directory which not ending with
I've tried something like
(find-lisp-find-files root "^(?!.*pyc$).*$") but it does not return any possible result.
You can use
(require 'cl-lib) (cl-remove-if (lambda (file) (or (file-directory-p file) (string-match "\\.py[co]$" file))) (directory-files default-directory))
It maybe that you have already the
(require 'cl-lib) in your initialization. In that case you do not need it here.
There are many ways to modify the above code segment. For an example you can add a regular expression filter to
directory-files. See the help for
directory-files. You can also wrap this segment with a
defun for easy use. You can also replace
directory-files-recursively if you want to recurse into sub-directories.
An example for a (sparsely tested) command
list-files with optional regular expressions for filtering files in and out:
(require 'cl-lib) (require 'dired) (defun dired-files-noselect (dir files) "Print files from list FILES in directory DIR using `dired-mode'." (with-current-buffer (get-buffer-create (directory-file-name dir)) (let ((inhibit-read-only t)) (cd dir) (delete-region (point-min) (point-max)) (insert " " default-directory (format ":\n dired-files %d\n" (length files))) (dolist (file files) (let ((a (file-attributes file 'string))) (insert (format " %s %d %s %s %6d %s %s\n" (nth 8 a) ; file modes like ls -l (nth 1 a) ; number of links to file (nth 2 a) ; uid as string (nth 3 a) ; gid as string (or (nth 7 a) 0) ; size in bytes (format-time-string "%d. %b %Y" (nth 5 a)) ; modification time file)))) (dired-mode) (dired-build-subdir-alist) (buffer-name)))) (defun dired-files (dir files) "Print files from list FILES in directory DIR using `dired-mode'." (display-buffer (dired-files-noselect dir files))) (defconst list-files-options '((:directory default-directory "files of this directory are listed" read-directory-name "Directory: " nil nil t) (:recursively nil "recurse into sub-directories" y-or-n-p "Recurse into sub-directories (y/n)?: ") (:include ".*" "files to list must match this regular expression or fulfill this predicate" read-regexp "Include files matching: ") (:exclude nil "files that match this regular expression or fulfill this predicate are filtered out" read-regexp "Exclude files matching: ") (:include-directories nil "directories are included in the listing if this option is non-nil" y-or-n-p "Include sub-directories (y/n)?: ")) "Alist of options for `list-files'. Each option has the structure (key . (default-value doc-string)).") (eval `(defun list-files (&rest options) ,(concat "List files. Options (with default value in parentheses): " (mapconcat (lambda (opt) (concat (symbol-name (car opt)) " (" (prin1-to-string (cadr opt)) ") " (caddr opt))) list-files-options "\n") " Example: (list-files :exclude \"\\\\.py[co]$\")") (interactive (let (opt options) (while (null (string-equal "" (setq opt (completing-read "Option (try tab-completion): " (mapcar (lambda (opt) (substring (symbol-name (car opt)) 1)) list-files-options) nil t)))) (setq opt (assoc-string (concat ":" opt) list-files-options)) (setq options (append (list (car opt) (apply #'funcall (nthcdr 3 opt))) options))) options)) (let* ((dir (plist-get options :directory)) (dir (if dir dir default-directory)) (include (plist-get options :include)) (exclude (plist-get options :exclude)) (full (plist-get options :full)) (file-list (if (plist-get options :recursively) (directory-files-recursively dir (or (and (stringp include) include) ".*") (plist-get options :include-directories)) (directory-files dir t (and (stringp include) include)))) (file-list (if (functionp include) (cl-remove-if-not include file-list) file-list)) (file-list (if exclude (cl-remove-if (if (functionp exclude) exclude `(lambda (file) (string-match ,exclude file))) file-list) file-list)) (file-list (if full file-list (let ((default-directory dir)) (mapcar #'file-relative-name file-list))))) (when (called-interactively-p 'any) (dired-files dir file-list)) file-list)))
The way you intended this to be done:
(find-lisp-find-files "." "\\(^..?\\|py[^co]\\|[^p]..\\|.[^y].\\)$")
which, probably requires some explanation.
Why your attempt didn't work
Emacs doesn't have zero-width assertions in regular expressions. Also, you need to escape the parenthesis in Emacs regexp.
What does the regular expression match
It matches three characters at the end of the file name. There are three cases:
pyfollowed by something that is neither
pfollowed by something that is not
y, followed by any character.
- any character but
yand then any other character.
thus ensuring anything that ends in either
pyo will not match, but everything else will.
PS. You might want to check M-x
Elisp regular expressions are hard to read, hard to get right and even harder to maintain. Here are two alternative approaches:
foreign-regexp.el -- https://github.com/k-talo/foreign-regexp.el
rx -- built-in macro, see its documentation string and examples at http://francismurillo.github.io/2017-03-30-Exploring-Emacs-rx-Macro/
This macro takes a readable description of the matches needed and generates the regexp. Anyone can go and modify the description without getting lost in the forest of backlashes.
The following is a reworking of previous examples. Changing
if lists matching files.
(delq nil (mapcar (lambda (i) (unless (string-match-p (rx ".py" (any "c" "o") eos ) i) i)) (directory-files default-directory)))