开发者

Idiomatic batch processing of text in Emacs?

In Python, you might do something like

fout = open('out','w')
fin = open('in')
for line in fin:
    fout.write(process(line)+"\n")
fin.close()
fout.close()

(I think it would be similar in many other languages as well). In Emacs Lisp, would you do something like

(find-file 'out')
(setq fout 开发者_JAVA百科(current-buffer)
(find-file 'in')
(setq fin (current-buffer)
(while moreLines
 (setq begin (point))
 (move-end-of-line 1)
 (setq line (buffer-substring-no-properties begin (point))
 ;; maybe
 (print (process line) fout)
 ;; or
 (save-excursion 
  (set-buffer fout)
  (insert (process line)))
 (setq moreLines (= 0 (forward-line 1))))
(kill-buffer fin)
(kill-buffer fout)

which I got inspiration (and code) from Emacs Lisp: Process a File line-by-line. Or should I try something entirely different? And how to remove the "" from the print statement?


If you actually want batch processing of stdin and sending the result to stdout, you can use the --script command line option to Emacs, which will enable you to write code that reads from stdin and writes to stdout and stderr.

Here is an example program which is like cat, except that it reverses each line:

#!/usr/local/bin/emacs --script
;;-*- mode: emacs-lisp;-*-

(defun process (string)
  "just reverse the string"
  (concat (nreverse (string-to-list string))))

(condition-case nil
    (let (line)
      ;; commented out b/c not relevant for `cat`, but potentially useful
      ;; (princ "argv is ")
      ;; (princ argv)
      ;; (princ "\n")
      ;; (princ "command-line-args is" )
      ;; (princ command-line-args)
      ;; (princ "\n")

      (while (setq line (read-from-minibuffer ""))
        (princ (process line))
        (princ "\n")))
  (error nil))

Now, if you had a file named stuff.txt which contained

abcd
1234
xyz

And you invoked the shell script written above like so (assuming it is named rcat):

rcat < stuff.txt

you will see the following printed to stdout:

dcba
4321
zyx

So, contrary to popular belief, you can actually do batch file processing on stdin and not actually have to read the entire file in at once.


Here's what I came up with. Looks a lot more idiomatic to me:

(with-temp-buffer
  (let ((dest-buffer (current-buffer)))
    (with-temp-buffer
      (insert-file-contents "/path/to/source/file")
      (while (search-forward-regexp ".*\n\\|.+" nil t)
        (let ((line (match-string 0)))
          (with-current-buffer dest-buffer
            (insert (process line)))))))
  (write-file "/path/to/dest/file" nil))


Emacs Lisp is not suitable for processing file-streams. The whole file must be read at once:

(defun my-line-fun (line)
  (concat "prefix: " line))

(let* ((in-file "in")
       (out-file "out")
       (lines (with-temp-buffer 
        (insert-file-contents in-file)
        (split-string (buffer-string)  "\n\r?"))))
  (with-temp-file out-file
    (mapconcat 'my-line-fun lines "\n")))
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜