Идиоматическая пакетная обработка текста в 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")))