Идиоматическая пакетная обработка текста в Emacs?

В Python вы можете сделать что-то вроде

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

(Я думаю, что это будет похоже на многие другие языки). В Emacs Lisp, вы бы сделали что-то вроде

(find-file 'out')
(setq fout (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)

который я получил от Emacs Lisp: и обработал файл построчно. Или я должен попробовать что-то совершенно другое? И как убрать "" из заявления на печать?

3 ответа

Решение

Если вы действительно хотите пакетную обработку stdin и отправив результат stdoutвы можете использовать опцию командной строки --script для Emacs, которая позволит вам писать код, который читает из stdinи пишетstdoutа такжеstderr,

Вот пример программы, которая похожаcatза исключением того, что он переворачивает каждую строку:

#!/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))

Теперь, если у вас был файл с именем stuff.txt который содержал

abcd
1234
xyz

И вы вызвали сценарий оболочки, написанный выше, так (если он называется rcat):

rcat < stuff.txt

вы увидите следующее напечатанное на стандартный вывод:

dcba
4321
zyx

Таким образом, вопреки распространенному мнению, вы можете сделать пакетную обработку файлов на stdin и на самом деле не нужно читать весь файл сразу.

Вот что я придумал. Выглядит намного более идиоматично для меня:

(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 не подходит для обработки файловых потоков. Весь файл должен быть прочитан сразу:

(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")))
Другие вопросы по тегам