Bagaimana cara membedakan output dari dua perintah?

165

Saya membayangkan cara paling sederhana untuk membandingkan isi dari dua direktori yang sama akan menjadi sesuatu seperti

diff `ls old` `ls new`

Tetapi saya mengerti mengapa ini tidak berhasil; diffsedang diserahkan daftar panjang file di baris perintah, bukan dua aliran seperti yang saya harapkan. Bagaimana cara melewatkan kedua output untuk berbeda secara langsung?

Ternary
sumber

Jawaban:

246

Substitusi perintah mengganti `…`output dari perintah ke baris perintah, jadi difflihatlah daftar file di kedua direktori sebagai argumen. Yang Anda inginkan adalah untuk diffmelihat dua nama file pada baris perintahnya, dan isi dari file-file ini menjadi daftar direktori. Itulah yang dilakukan proses substitusi .

diff <(ls old) <(ls new)

Argumen untuk diffakan terlihat seperti /dev/fd/3dan /dev/fd/4: mereka adalah deskriptor file yang sesuai dengan dua pipa yang dibuat oleh bash. Saat diffmembuka file-file ini, itu akan terhubung ke sisi baca dari setiap pipa. Sisi tulis setiap pipa terhubung ke lsperintah.

Gilles
sumber
49
echo <(echo) <(echo)tidak pernah berpikir ini bisa sangat menarik: D
Aquarius Power
3
Substitusi proses tidak didukung oleh semua shell , tetapi pengalihan pipa adalah solusi yang rapi .
Irfan434
1
Hanya untuk menyebutkan bahwa penguraian ls tidak disarankan unix.stackexchange.com/questions/128985/why-not-parse-ls
Katu
@ Katu Masalahnya lsadalah bahwa file nama mangles. Parsing hasilnya rapuh (tidak bekerja dengan nama file "aneh"). Untuk membandingkan dua daftar direktori, tidak apa-apa asalkan outputnya tidak ambigu. Dengan nama file yang sewenang-wenang, ini akan membutuhkan opsi seperti --quoting-style=escape.
Gilles
1
@akan <(…)membuat pipa. Tampaknya berbaur tidak bekerja dengan pipa, jadi Anda tidak dapat menggunakan <(…). Di zsh, Anda dapat menggantinya <(…)dengan =(…)dan itu akan berfungsi karena =(…)menempatkan output antara dalam file sementara. Dalam bash saya tidak berpikir ada sintaks yang mudah digunakan, Anda harus mengelola sendiri file-file sementara.
Gilles
3

Untuk zsh, menggunakan =(command)secara otomatis membuat file sementara dan menggantikan =(command)dengan jalur file itu sendiri. Dengan Substitusi Perintah, $(command)diganti dengan output dari perintah.

Jadi ada tiga opsi:

  1. Substitusi Perintah: $(...)
  2. Substitusi proses: <(...)
  3. Pergantian Proses dengan Zsh-Flavoured: =(...)

Subsitusi proses rasa zsh, # 3, sangat berguna dan dapat digunakan seperti itu untuk membandingkan output dari dua perintah menggunakan alat diff, misalnya Beyond Compare:

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

Untuk Beyond Compare, perhatikan bahwa Anda harus menggunakan bcompuntuk yang di atas (bukan bcompare) sejak bcompmeluncurkan perbandingan dan menunggu untuk menyelesaikan. Jika Anda menggunakan bcompare, itu meluncurkan perbandingan dan segera keluar karena file sementara yang dibuat untuk menyimpan output dari perintah menghilang.

Baca lebih lanjut di sini: http://zsh.sourceforge.net/Intro/intro_7.html

Perhatikan juga ini:

Perhatikan bahwa shell membuat file sementara, dan menghapusnya ketika perintah selesai.

dan berikut ini yang merupakan perbedaan antara dua jenis subtitusi Proses yang didukung oleh zsh (yaitu # 2 dan # 3):

Jika Anda membaca halaman manual zsh, Anda mungkin memperhatikan bahwa <(...) adalah bentuk lain dari proses substitusi yang mirip dengan = (...). Ada perbedaan penting antara keduanya. Dalam kasus <(...), shell membuat pipa bernama (FIFO) alih-alih file. Ini lebih baik, karena tidak mengisi sistem file; tetapi tidak bekerja dalam semua kasus. Sebenarnya, jika kita telah mengganti = (...) dengan <(...) pada contoh di atas, semuanya akan berhenti bekerja kecuali untuk fgrep -f <(...). Anda tidak dapat mengedit pipa, atau membukanya sebagai folder surat; Namun, fgrep tidak memiliki masalah dengan membaca daftar kata-kata dari sebuah pipa. Anda mungkin bertanya-tanya mengapa bilah diff <(foo) tidak berfungsi, karena foo | karya diff-bar; ini karena diff membuat file sementara jika memperhatikan bahwa salah satu argumennya adalah -, dan kemudian menyalin input standarnya ke file sementara.

Referensi: https://unix.stackexchange.com/questions/393349/difference-between-subshells-and-process-substitution

Ashutosh Jindal
sumber
2
$(...)bukan proses substitusi, itu substitusi perintah . <(...)adalah proses substitusi. Itu sebabnya bagian yang dikutip tidak menyebutkan $(...)sama sekali.
muru
2

Kulit ikan

Dalam shell Ikan Anda harus pipa ke psub . Berikut adalah contoh perbandingan konfigurasi heroku dan dokku dengan Beyond Compare :

bcompare (ssh [email protected] dokku config myapp | sort | psub) (heroku config -a myapp | sort | psub)
WooYek
sumber
1
Alat diff grafis lainnya adalah meldyang open source dan tersedia di repositori Ubuntu dan EPEL. meldmerge.org
phiphi
0

Saya sering menggunakan teknik yang dijelaskan dalam jawaban yang diterima:

diff <(ls old) <(ls new)

tetapi saya menemukan bahwa saya biasanya menggunakannya dengan perintah yang jauh lebih kompleks daripada contoh di atas. Dalam kasus-kasus seperti itu dapat mengganggu untuk membuat perintah diff. Saya telah menemukan beberapa solusi yang mungkin berguna bagi orang lain.

Saya menemukan bahwa 99% dari waktu saya mencoba perintah yang relevan sebelum menjalankan diff. Konsekuensinya, perintah yang ingin saya perbaiki ada di sana dalam riwayat saya ... mengapa tidak menggunakannya?

Saya menggunakan bash builtin Fix Command (fc) untuk menjalankan dua perintah terakhir:

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

Bendera fc adalah:

-n : Tidak ada nomor. Ini menekan nomor perintah saat mendaftar.

-l : Listing: Perintah-perintah terdaftar pada output standar.

yang -1 -1mengacu pada awal dan akhir positing dalam sejarah, dalam hal ini yang dari perintah terakhir untuk perintah terakhir yang menghasilkan hanya perintah terakhir.

Terakhir kita bungkus ini $()untuk menjalankan perintah dalam subkulit.

Jelas ini agak sulit untuk diketik sehingga kita dapat membuat alias:

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

Atau kita dapat membuat fungsi:

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") )
}

yang mendukung menentukan garis sejarah yang akan digunakan. Setelah menggunakan keduanya saya menemukan alias adalah versi yang saya sukai.

htaccess
sumber