Diff von zwei Puffern ohne temporäre Dateien zu erstellen

9

Ich brauche den Unterschied von zwei Puffern. Eine Möglichkeit wäre, temporäre Dateien zu erstellen, die den Inhalt dieser Puffer enthalten, und die diffFunktion zu verwenden. Die Puffer enthalten jedoch vertrauliche Informationen, und ich würde es vorziehen, diese Informationen nicht im Klartext auf der Festplatte zu haben.

Ich habe über die Verwendung von ediff nachgedacht, mit dem Puffer direkt verglichen werden können, aber ediff startet eine interaktive Sitzung und möchte diese in einem Skript verwenden.

tmalsburg
quelle
Um dies zu verdeutlichen, möchten Sie eine Funktion, die den Unterschied zwischen zwei Puffern ohne Benutzerinteraktion ergibt?
user2699
@ user2699, genau. Kontext: emacs.stackexchange.com/questions/27349/…
tmalsburg
Ist es möglich, dafür Named Pipes zu verwenden?
Tmalsburg
1
Ich bin nicht mit Named Pipes vertraut, aber es scheint, dass die beste Lösung etwas jenseits von Emacs wäre. Wenn Sie den Quellcode von ediff-buffersganz kurz betrachten, werden anscheinend Puffer in temporären Dateien auf der Festplatte gespeichert und anschließend das Systemdiff-Dienstprogramm für diese Dateien aufgerufen, sodass es keinen praktischen Unterschied gibt, sich diffselbst aufzurufen .
user2699
2
Unter welchem ​​Betriebssystem? Die Diff-Funktion verwendet ein externes Programm und die Kommunikation mit einem externen Programm ist vom Betriebssystem abhängig. Die einfache Lösung wäre, die Dateien in einem speicherinternen Dateisystem zu speichern. Diese sind heutzutage Standard unter Linux, existieren aber möglicherweise nicht auf anderen Plattformen.
Gilles 'SO - hör auf böse zu sein'

Antworten:

3

@tmalsburg, Folgende Befehlsaufrufe unterscheiden sich in 2 Puffern ohne die Erstellung temporärer Dateien. Es werden Named Pipes verwendet, wie Sie oben vorgeschlagen haben:

(require 'diff)
(defun diff-buffers-without-temp-files (buffer1 buffer2 &optional switches)
  "Run diff program on BUFFER1 and BUFFER2.
Make the comparison without the creation of temporary files.

When called interactively with a prefix argument, prompt
interactively for diff switches.  Otherwise, the switches
specified in the variable `diff-switches' are passed to the diff command."
  (interactive
   (list (read-buffer "buffer1: " (current-buffer))
         (read-buffer "buffer2: " (current-buffer))
         (diff-switches)))
  (or switches (setq switches diff-switches))
  (unless (listp switches) (setq switches (list switches)))
  (let ((buffers (list buffer1 buffer2))
        (buf (get-buffer-create "*diff-buffers*"))
        fifos res)
    (dotimes (_ 2) (push (make-temp-name "/tmp/pipe") fifos))
    (setq fifos (nreverse fifos))
    (with-current-buffer buf (erase-buffer))
    (unwind-protect
        (progn
          (dotimes (i 2)
            (let ((cmd (format "cat > %s << EOF\n%s\nEOF"
                               (nth i fifos)
                               (with-current-buffer (nth i buffers)
                                 (buffer-string)))))
              (call-process "mkfifo" nil nil nil (nth i fifos))
              (start-process-shell-command (format "p%d" i) nil cmd)))
          (setq res (apply #'call-process diff-command nil buf nil (car fifos) (cadr fifos) switches))
          (if (zerop res)
              (message "Buffers have same content")
            (display-buffer buf)
            (with-current-buffer buf (diff-mode))
            (message "Buffer contents are different"))
          res)
      ;; Clean up.
      (dolist (x fifos)
        (and (file-exists-p x) (delete-file x))))))
  1. Bei interaktivem Aufruf wird der Unterschied angezeigt, wenn die Puffer unterschiedliche Inhalte haben.
  2. Beim Aufruf von Lisp wird der Exit-Code des diff-Programms zurückgegeben. Das heißt, 0, wenn die Puffer denselben Inhalt haben, andernfalls 1.

    (diff-buffers-without-temp-files (get-buffer "*scratch*") (get-buffer "*scratch*"))
    => 0
    
    
    (diff-buffers-without-temp-files (get-buffer "*scratch*") (get-buffer "*Messages*"))
    => 1
    

Getestet für Emacs Version 24.3 auf einem Computer mit Debian GNU / Linux 9.0 (Stretch).

  • Der obige Code scheint zu funktionieren, wenn er von Lisp aufgerufen wird. Leider zeigt sich bei interaktiven Anrufen meistens ein abgeschnittener Unterschied.

  • In der folgenden Version wird die asynchrone Bibliothek eines Drittanbieters verwendet . Die Unterschiede werden nicht abgeschnitten.

(require 'diff)
(require 'async)
(defun diff-buffers-without-temp-files (buffer1 buffer2 &optional switches)
  "Run diff program on BUFFER1 and BUFFER2.
Make the comparison without the creation of temporary files.

When called interactively with a prefix argument, prompt
interactively for diff switches.  Otherwise, the switches
specified in the variable `diff-switches' are passed to the diff command."
  (interactive
   (list (read-buffer "buffer1: " (current-buffer))
         (read-buffer "buffer2: " (current-buffer))
         (diff-switches)))
  (or switches (setq switches diff-switches))
  (unless (listp switches) (setq switches (list switches)))
  (let ((buffers (list buffer1 buffer2))
        (buf (get-buffer-create "*diff-buffers*"))
        fifos res)
    (dotimes (_ 2) (push (make-temp-name "/tmp/pipe") fifos))
    (setq fifos (nreverse fifos))
    (with-current-buffer buf (erase-buffer))
    (unwind-protect
        (progn
          (dotimes (i 2)
            (let ((cmd (format "cat > %s" (nth i fifos))))
              (call-process "mkfifo" nil nil nil (nth i fifos))
              (async-start
               `(lambda ()
                  (with-temp-buffer
                    (insert ,(with-current-buffer (nth i buffers) (buffer-string)))
                    (call-process-region
                     1 (point-max) shell-file-name nil nil nil
                     shell-command-switch ,cmd))))))
          (setq res (apply #'call-process diff-command nil buf nil (car fifos) (cadr fifos) switches))
          (if (zerop res)
              (message "Buffers have same content")
            (display-buffer buf)
            (with-current-buffer buf (diff-mode))
            (message "Buffer contents are different"))
          res)
      ;; Clean up.
      (dolist (x fifos)
        (and (file-exists-p x) (delete-file x))))))
Tino
quelle
Danke, @tino, genau das habe ich gesucht!
Tmalsburg
0

AFAIU Emacs benötigt externe Programme, um den Diff zu machen. Zum Beispiel ediff-make-diff2-buffer, die zwei Puffer vergleichen würde, ruft intern auf

  (ediff-exec-process ediff-diff-program
                 diff-buffer
                 'synchronize
                 ediff-actual-diff-options file1 file2)

Wo ediff-diff-programkönnte die GNU / Linux darstellen diff. Mit dem neuen FFI ist das System diffmöglicherweise zugänglich. Auch eine Emacs Lisp Diff-Implementierung könnte den Job erledigen.

Andreas Röhler
quelle
0

Wie wäre es, wenn Sie den Befehl shell verwenden, um diff aufzurufen und ihm einen Ausgabepuffer zu übergeben? Oder Shell-Befehl-zu-Zeichenfolge, um den Unterschied in einer Zeichenfolge zu erhalten

Russell
quelle
Ich bin nicht sicher, ob ich verstehe, was Sie damit meinen, dass Sie ihm einen Ausgabepuffer übergeben. Könnten Sie das bitte etwas näher erläutern? Vielen Dank.
Tmalsburg
Die Syntax des Shell-Befehls lautet (Shell-Befehl COMMAND & optional OUTPUT-BUFFER ERROR-BUFFER). Das optionale zweite Argument lautet: "" Das optionale zweite Argument OUTPUT-BUFFER sagt, wenn es nicht Null ist, dass die Ausgabe in einen anderen Puffer gestellt werden soll. Wenn OUTPUT-BUFFER ein Puffer oder Puffername ist, legen Sie die Ausgabe dort ab. Wenn OUTPUT -BUFFER ist kein Puffer und nicht Null. Fügen Sie die Ausgabe in den aktuellen Puffer ein. (Dies kann nicht asynchron erfolgen.) In beiden Fällen wird der Puffer zuerst gelöscht und die Ausgabe nach dem Punkt eingefügt (hinterlässt eine Markierung). "" "
Russell
0

Wenn Sie mit Ediff einverstanden sind, reicht dies aus:

(defun my/diff-buffers (buffer-A buffer-B)
  "Run Ediff on a pair of buffers, BUFFER-A and BUFFER-B."
  (ediff-buffers-internal buffer-A buffer-B nil nil 'ediff-buffers))

und nenne es so:

(my/diff-buffers "*temp*" "*temp*<2>")
Yasushi Shoji
quelle
Danke für die Antwort. Bei dieser Lösung gibt es zwei Probleme. 1. Meines Wissens verwendet ediff diff und benötigt daher temporäre Dateien. 2. Ihre Lösung startet eine interaktive Sitzung, aber ich möchte nur den Unterschied in einem Puffer oder einer Zeichenfolge.
Tmalsburg
1
Ah, ich habe deine Frage falsch gelesen. Ich dachte, Sie möchten keine temporären Dateien selbst erstellen. emacs.stackexchange.com/questions/19812 fragt nach der elisp-Version von diff. Keine Erwähnung über temporäre Dateien. Wenn Sie Ihre Frage noch einmal lesen, müssen Sie eine diff-Ausgabe haben? oder möchten Sie einfach Ihre Zeichenfolge vergleichen und den Unterschied kennen? Ein Anruferbeispiel könnte helfen.
Yasushi Shoji
(defun my/diff-buffers (buffer-A buffer-B) "Run Ediff on a pair of buffers, BUFFER-A and BUFFER-B." (interactive (list (read-buffer "buffer1: " (current-buffer)) (read-buffer "buffer2: " (current-buffer)))) (ediff-buffers-internal buffer-A buffer-B nil nil 'ediff-buffers))
HappyFace