Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Executing rgrep non-interactively

Tags:

emacs

I'm trying to run the rgrep command from a small Emacs Lisp utility, but I'm getting an odd error. The command description is:

rgrep is an interactive compiled Lisp function.

(rgrep REGEXP &optional FILES DIR CONFIRM)

Recursively grep for REGEXP in FILES in directory tree rooted at DIR. The search is limited to file names matching shell pattern FILES. FILES may use abbreviations defined in grep-files-aliases', e.g. enteringch' is equivalent to `*.[ch]'.

With C-u prefix, you can edit the constructed shell command line before it is executed. With two C-u prefixes, directly edit and run `grep-find-command'.

Collect output in a buffer. While find runs asynchronously, you can use C-x ` (M-x next-error), or RET in the grep output buffer, to go to the lines where grep found matches.

This command shares argument histories with M-x lgrep and M-x grep-find.

I try to run:

(rgrep "something" "all" "~/projects/")

and I get

 *** Eval error ***  Wrong type argument: stringp, nil

Obviously all the params are strings, so I cannot understand where is this nil coming from.

I'm running Emacs 23.3 on Debian Testing.

Thanks in advance for your help!

like image 402
Bozhidar Batsov Avatar asked Aug 10 '11 16:08

Bozhidar Batsov


4 Answers

According to the Emacs manual:

The commands M-x lgrep (local grep) and M-x rgrep (recursive grep) are more user-friendly versions of grep and grep-find, which prompt separately for the regular expression to match, the files to search, and the base directory for the search.

For the problem at hand, we don't need this 'user-friendliness' which gets in the way. We can use the plain "grep" elisp function which is better suited for non-interactive use. Basically this function takes as argument whatever grep command line you'd have to use to achieve the desired result. Maximum flexibility!

Here's what it would look like for your scenario:

(grep "grep --color -rn something ~/projects/")

Here's another, more complex use of grep's options, to match only complete words in Python files:

(grep "grep --include=\"*.py\" --color -rnw your_pattern files_root_dir")
like image 139
Apteryx Avatar answered Oct 16 '22 05:10

Apteryx


i think it's because you have no 'grep-find-template' defined. this is certainly (having debugged) why the command produces the error on my version. look at the help for that variable.

cheers.

ps. it's the difference between calling it interactively or not..

"find . <X> -type f <F> -print0 | \"xargs\" -0 -e grep <C> -nH -e <R>"

..gets set by 'grep-calc-defaults' when called interactively

pps. i think you just have to be careful with your call. if no matches are found, you'll get a 'Grep exited abnormally with code 123' error.

mkdir -p ~/a/b
cp ~/.bash* ~/a/b
emacs -q

C-x b <RET> *scratch*
(grep-compute-defaults)
(rgrep "^.*\\?\=.*$" "*bash*" "~/a")

..lots of matches!

like image 5
elbeardmorez Avatar answered Oct 21 '22 19:10

elbeardmorez


The reason you get this when you call rgrep programmatically is that all the interactive calls to the variants of grep have a call to grep-compute-defaults in the interactive call. This does not get evaluated when you're calling programmatically.

The easiest way to fix this is to add

(eval-after-load "grep"
  '(grep-compute-defaults))

in your code, which will force that to be called (but only when needed).

like image 4
Trey Jackson Avatar answered Oct 21 '22 18:10

Trey Jackson


The following appears to work ok for me:

(defadvice rgrep (around rgrep-init)
  "Init grep defaults before calling rgrep non-interactively."
  (when (not (called-interactively-p))
    (grep-compute-defaults))
  ad-do-it)

(ad-activate 'rgrep)

(rgrep "something" "all" "~/projects/")
like image 2
zev Avatar answered Oct 21 '22 18:10

zev