2つのコマンドの出力を比較するにはどうすればよいですか?


165

2つの類似したディレクトリの内容を比較する最も簡単な方法は次のようになると想像していました

diff `ls old` `ls new`

しかし、なぜこれが機能しないのかわかります。diff私が望んでいたような2つのストリームではなく、コマンドラインでファイルの大きな長いリストを渡されています。2つの出力を直接diffに渡すにはどうすればよいですか?


回答:


246

コマンド置換`…`は、コマンドの出力をコマンドラインに置換するためdiff、両方のディレクトリ内のファイルのリストが引数として表示されます。必要なのはdiff、コマンドラインに2つのファイル名を表示し、これらのファイルの内容をディレクトリリストにすることです。それがプロセスの置換です。

diff <(ls old) <(ls new)

の引数diffは次のように/dev/fd/3なり/dev/fd/4ます:bashによって作成された2つのパイプに対応するファイル記述子です。ときはdiff、これらのファイルを開くには、各パイプの読み出し側に接続されます。各パイプの書き込み側はlsコマンドに接続されています。


49
echo <(echo) <(echo)これがこんなに面白いとは思わなかった:D
Aquarius Power 14

3
プロセスの置換はすべてのシェルサポートされているわけではありませんが、パイプリダイレクトは適切な回避策です。
Irfan434

1
ただ、LSを解析することは推奨されないことは言うまでもunix.stackexchange.com/questions/128985/why-not-parse-ls
Katu

@Katu問題lsは、ファイル名が破損することです。出力の解析は脆弱です(「奇妙な」ファイル名では機能しません)。2つのディレクトリリストを比較する場合、出力が明確である限り問題ありません。任意のファイル名の場合、これにはなどのオプションが必要です--quoting-style=escape
ジル

1
@will <(…)はパイプを作成します。meldはパイプでは機能しないため、を使用できないよう<(…)です。zshのでは、あなたは置き換えることができます<(…)による=(…)と、ので、それは動作します=(…)一時ファイルに中間出力を置きます。bashでは、便利な構文はないと思います。一時ファイルを自分で管理する必要があります。
ジル

3

zshの場合、使用=(command)すると一時ファイルが自動的に作成され=(command)、ファイル自体のパスに置き換えられます。コマンド置換で$(command)は、コマンドの出力に置き換えられます。

したがって、3つのオプションがあります。

  1. コマンド置換: $(...)
  2. プロセス置換: <(...)
  3. zshフレーバープロセスの置換: =(...)

zsh風味のプロセス置換、#3は非常に便利であり、比較ツールなどを使用して2つのコマンドの出力を比較するために、たとえばBeyond Compareのように使用できます。

bcomp  =(ulimit -Sa | sort) =(ulimit -Ha | sort)

比較を超える場合は、比較を開始し、比較が完了するまで待機するbcompため、(ではなくbcompare)上記で使用する必要があることに注意してください。を使用すると、比較が開始され、すぐに終了します。これにより、コマンドの出力を保存するために作成された一時ファイルが消えます。bcompbcompare

詳細はこちら:http : //zsh.sourceforge.net/Intro/intro_7.html

これにも注意してください:

シェルは一時ファイルを作成し、コマンドが終了すると削除することに注意してください。

また、zshでサポートされている2つのタイプのプロセス置換(#2と#3)の違いは次のとおりです。

zshのマニュアルページを読むと、<(...)は=(...)に似たプロセス置換の別の形式であることに気付くかもしれません。2つの間に重要な違いがあります。<(...)の場合、シェルはファイルの代わりに名前付きパイプ(FIFO)を作成します。これは、ファイルシステムをいっぱいにしないため、より優れています。しかし、すべての場合に機能するわけではありません。実際、上記の例で=(...)を<(...)に置き換えた場合、fgrep -f <(...)を除くすべての機能が停止します。パイプを編集したり、メールフォルダーとして開いたりすることはできません。ただし、fgrepは、パイプから単語のリストを読み取ることに問題はありません。foo <(foo)barがなぜ機能しないのか疑問に思うかもしれません。diff-バーは機能します。これは、diffが引数の1つが-であることに気付いた場合に一時ファイルを作成し、その標準入力を一時ファイルにコピーするためです。

参照:https : //unix.stackexchange.com/questions/393349/difference-between-subshel​​ls-and-process-substitution


2
$(...)プロセス置換ではなく、コマンド置換です。<(...)プロセス置換です。それが、引用された一節にまったく言及さ$(...)れていない理由です。
ムル

2

魚の殻

Fishシェルでは、psubにパイプする必要があります。Beyond Compareとのherokuおよびdokkuの構成比較の例を次に示します。

bcompare (ssh me@myapp.pl dokku config myapp | sort | psub) (heroku config -a myapp | sort | psub)

1
もう1つのグラフィカルなdiffツールはmeld、オープンソースであり、UbuntuおよびEPELリポジトリで利用可能です。meldmerge.org
phiphi

0

私はよく受け入れられた答えで説明されているテクニックを使用します:

diff <(ls old) <(ls new)

ただし、通常は上記の例よりもはるかに複雑なコマンドで使用します。このような場合、diffコマンドを作成するのは面倒です。私は他の人が役に立つと思うかもしれないいくつかの解決策を考え出しました。

diffを実行する前に、関連するコマンドを試してみる時間の99%が見つかりました。その結果、私が比較したいコマンドは私の歴史の中にあります...なぜそれらを使用しませんか?

Fix Command(fc)bashビルトインを使用して、最後の2つのコマンドを実行します。

$ echo A
A
$ echo B
B
$ diff --color <( $(fc -ln -1 -1) ) <( $(fc -ln -2 -2 ) )
1c1
< B
---
> A

fcフラグは次のとおりです。

-n:番号なし。リストするときにコマンド番号を抑制します。

-l:リスト:コマンドは標準出力にリストされます。

-1 -1この場合には、歴史の中で開始と終了のポジショニングを参照して、最後のコマンドからちょうど最後のコマンドを生成する最後のコマンドに。

最後にこれをラップ$()して、サブシェルでコマンドを実行します。

明らかに、これは入力するのが少し面倒なので、エイリアスを作成できます。

alias dl='diff --color <( $(fc -ln -1 -1) ) <( $(fc -ln -2 -2 ) )'

または、関数を作成できます。

dl() {
    if [[ -z "$1" ]]; then
        first="1"
    else
        first="$1"
    fi
    if [[ -z "$2" ]]; then
        last="2"
    else
        last="$2"
    fi
    # shellcheck disable=SC2091
    diff --color <( $(fc -ln "-$first" "-$first") ) <( $(fc -ln "-$last" "-$last") )
}

使用する履歴行の指定をサポートします。両方を使用した後、エイリアスが好みのバージョンであることがわかりました。

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