一時ファイルを作成しない2つのバッファーの差分


9

2つのバッファーの差分が必要です。1つの方法は、これらのバッファーの内容を含む一時ファイルを作成し、diff関数を使用することです。ただし、バッファには機密情報が含まれているので、ディスク上にその情報をクリアテキストで保存しないようにします。

バッファを直接比較できるediffの使用を考えましたが、ediffはインタラクティブセッションを開始し、これをスクリプトで使用したいと考えています。


明確にするために、ユーザーの操作なしで2つのバッファーの差分を提供する関数が必要ですか?
user2699 2016

@ user2699、正確に。コンテキスト:emacs.stackexchange.com/questions/27349/...
tmalsburg

これに名前付きパイプを使用することは可能ですか?
tmalsburg 2016

1
私は名前付きパイプに精通していませんが、最良の解決策はemacsを超えるものになると思われます。ediff-buffersごく簡単にソースコードを見ると、バッファをディスク上の一時ファイルに保存してから、それらのファイルに対してシステム差分ユーティリティを呼び出すので、diff自分を呼び出すことと実際的な違いはありません。
user2699 2016

2
どのオペレーティングシステムの下で?diff関数は外部プログラムを使用し、外部プログラムとの通信はOSに依存します。簡単な解決策は、ファイルをメモリ内ファイルシステムに保存することです。これらは最近のLinuxでは標準ですが、他のプラットフォームには存在しない場合があります。
Gilles「SO-邪悪なことをやめよう」

回答:


3

@tmalsburg、次のコマンドは、一時ファイルを作成せずに2つのバッファーでdiffを呼び出します。上記で提案したように、名前付きパイプを使用します。

(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. インタラクティブに呼び出されると、バッファーの内容が異なる場合に差分が表示されます。
  2. Lispから呼び出されると、diffプログラムの終了コードを返します。つまり、バッファの内容が同じ場合は0、それ以外の場合は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
    

Debian GNU / Linux 9.0(ストレッチ)を実行しているマシンでEmacsバージョン24.3をテストしました。

  • 上記のコードは、Lispから呼び出された場合に機能するようです。残念ながら、ほとんどの場合、対話型呼び出しで切り捨てられた差分が表示されます。

  • 次のバージョンでは、サードパーティの非同期ライブラリを使用しています。差分は切り捨てられません。

(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、これはまさに私が探していたものです!
tmalsburg 2017

0

AFAIU Emacsは、外部プログラムを使用してdiffを実行します。たとえばediff-make-diff2-buffer、2つのバッファを比較し、内部的に呼び出す

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

どこediff-diff-programがGNU / Linuxを表すかdiff。新しいFFIを使用すると、システムdiffにアクセスできる場合があります。また、Emacs Lisp diff-implementationでうまくいくかもしれません。


0

shell-commandを使用してdiffを呼び出し、出力バッファーに渡しますか?または、shell-command-to-stringを使用して、文字列の差分を取得します


出力バッファを渡すことで、あなたが何を言っているのか理解できません。これについてもう少し詳しく説明してもらえますか?ありがとう。
tmalsburg 2016年

shell-commandの構文は(shell-command COMMAND&optional OUTPUT-BUFFER ERROR-BUFFER)です。オプションの2番目の引数は次のとおりです: "" "オプションの2番目の引数OUTPUT-BUFFERは、nilでない場合、出力を別のバッファーに置くように指示します。OUTPUT-BUFFERがバッファーまたはバッファー名の場合、そこに出力を置きます。 -BUFFERはバッファではなく、nilではありません。現在のバッファに出力を挿入します(これは非同期では実行できません。)どちらの場合も、バッファは最初に消去され、出力はポイントの後に挿入されます(マークを後に残します)。 "" "
ラッセル

0

Ediffで問題がなければ、次のようになります。

(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))

次のように呼び出します。

(my/diff-buffers "*temp*" "*temp*<2>")

ご回答ありがとうございます。このソリューションには2つの問題があります。1.私の知る限り、ediffはdiffを使用しているため、一時ファイルが必要です。2.ソリューションがインタラクティブセッションを開始しますが、バッファまたは文字列の差分が必要です。
tmalsburg 16

1
ああ、あなたの質問を誤解しました。自分で一時ファイルを作成したくないと思いました。 emacs.stackexchange.com/questions/19812がelispバージョンのdiffを要求しています。ただし、一時ファイルについては触れません。もう一度質問を読んで、差分出力が必要ですか?または、文字列を比較して差分を知りたいだけですか?発信者の例が役立つ場合があります。
庄司靖

(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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.