Bagaimana cara menggabungkan dan mengirim hasil dari dua perintah yang berbeda ke perintah tunggal?

35

Saya ingin menggabungkan (gabungan) output dari dua perintah yang berbeda, dan menyalurkannya ke satu perintah.

Contoh konyol:

Perintah Saya ingin menggabungkan output:

cat wordlist.txt
ls ~/folder/*

ke:

wc -l

Dalam contoh ini, jika wordlist.txt berisi 5 baris dan 3 file, saya ingin wc -lmengembalikan 8.

$cat wordlist.txt *[magical union thing]* ls ~/folder/* | wc -l
8

Bagaimana aku melakukan itu?

David Oneill
sumber

Jawaban:

56

Persatuan ajaib Anda adalah titik koma ... dan kurung kurawal:

    { cat wordlist.txt ; ls ~/folder/* ; } | wc -l

Kurung kurawal hanya mengelompokkan perintah bersama, sehingga tanda pipa |mempengaruhi output gabungan.

Anda juga dapat menggunakan tanda kurung di ()sekitar grup perintah, yang akan menjalankan perintah dalam subkulit. Ini memiliki serangkaian perbedaan yang halus dengan kurung kurawal, misalnya coba yang berikut:

    cd $HOME/Desktop ; (cd $HOME ; pwd) ; pwd
    cd $HOME/Desktop ; { cd $HOME ; pwd ; } ; pwd

Anda akan melihat bahwa semua variabel lingkungan, termasuk direktori kerja saat ini, direset setelah keluar dari grup kurung, tetapi tidak setelah keluar dari grup kurung kurawal.

Adapun titik koma, alternatif termasuk &&dan ||tanda - tanda, yang secara kondisional akan mengeksekusi perintah kedua hanya jika yang pertama berhasil atau jika tidak, masing-masing, misalnya

    cd $HOME/project && make
    ls $HOME/project || echo "Directory not found."
pablomme
sumber
1
Itu berhasil! Bantu saya belajar - apa sebenarnya yang dilakukan kurung kurawal di sini? Kekuatan sihir apa lagi yang mereka miliki?
David Oneill
@ Davidvidne { list; }adalah perintah majemuk . From bash(1): list dijalankan di lingkungan shell saat ini. daftar harus diakhiri dengan baris baru atau titik koma. [..] Status kembali adalah status keluar dari daftar.
Lekensteyn
Saya telah menambahkan sedikit info lebih lanjut ke jawabannya. Panduan pasti untuk shell scripting dengan bash adalah manual bash, ketik man bashdari baris perintah dan ramban.
pablomme
haruskah; jika perintah itu tidak menjadi && jika file wordlist.txt tidak ada?
Rinzwind
Jika wordlist.txttidak ada kesalahan dari catakan muncul pada kesalahan standar, tetapi tidak pada output standar, jadi wc -ltidak akan menghitung garis apa pun darinya. Hal yang sama berlaku untuk yang ~/foldertidak ada. Satu dapat menambahkan 2> /dev/nullantara masing-masing perintah dan titik koma mereka untuk mencegah noise pada standard error, tetapi selain jelek, pesan error tidak berbahaya untuk tujuan penghitungan baris.
pablomme
8

Karena wcmenerima jalur file sebagai input, Anda juga dapat menggunakan subtitusi proses:

wc -l <(cat wordlist.txt; ls ~/folder/*)

Ini kira-kira sama dengan:

echo wordlist.txt > temp
ls ~/folder/* >> temp
wc -l temp

Pikiran yang ls ~/folder/*juga mengembalikan isi subdirektori jika ada (karena ekspansi glob). Jika Anda hanya ingin membuat daftar isi ~/folder, gunakan saja ls ~/folder.

Lekensteyn
sumber
Itu wc -lhanya contoh yang dibuat-buat, saya sebenarnya memipisnya menjadi sesuatu yang lebih rumit yang tidak memiliki opsi ini.
David Oneill
2
@ Davidvidne Nah, karena catmenerima argumen file, Anda dapat menggunakan cat <(cat wordlist.txt; ls wordlist.txt) | wc -ldan bahkan cat wordlist.txt <(ls wordlist.txt) | wc -l. Ini tentu saja sangat jelek, tetapi ini menunjukkan kemungkinan tanpa akhir dengan alat-alat baris perintah.
Lekensteyn
1
@ Davidvidne Benar, saya sudah memperbaikinya sekarang, terima kasih.
Lekensteyn
1
Anda ingin ls ~/folder/agar ~/foldertautan simbolik lsmencantumkan konten targetnya dan bukan tautan itu sendiri. Omong-omong, semua lsperintah harus diikuti -1sehingga satu file per baris dicetak dan wc -lmerupakan cara yang valid untuk menghitung file.
pablomme
@ pablomme Anda benar tentang hal symlink, tetapi -1tersirat karena outputnya bukan terminal tetapi pipa.
Lekensteyn
2

Saya bertanya pada diri sendiri pertanyaan yang sama, dan akhirnya menulis naskah pendek.

magicalUnionThing(Saya menyebutnya append):

#!/bin/sh
cat /dev/stdin
$*

Jadikan skrip itu dapat dieksekusi

chmod +x ./magicalUnionThing

Sekarang kamu lakukan

cat wordlist.txt |./magicalUnionThing ls ~/folder/* | wc -l

Apa fungsinya:

  • Kirim input standar ke output standar
  • Jalankan argumen. $*mengembalikan semua argumen sebagai string. Output dari perintah itu pergi ke output standar skrip secara default.

Jadi stdout dari magicalUnionThing akan menjadi stdin + stdout dari perintah yang dilewatkan sebagai argumen.

Tentu saja ada cara yang lebih sederhana, sesuai jawaban lain.
Mungkin alternatif ini dapat bermanfaat dalam beberapa kasus.

Rolf
sumber