Bagaimana cara menggunakan tee untuk mengalihkan ke grep

13

Saya tidak punya banyak pengalaman menggunakan tee, jadi saya harap ini tidak terlalu mendasar.

Setelah melihat salah satu jawaban untuk pertanyaan ini, saya menemukan beheviour aneh dengan tee.

Agar saya dapat menampilkan baris pertama, dan baris yang ditemukan, saya dapat menggunakan ini:

ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

Namun, pertama kali saya menjalankan ini (di zsh) hasilnya berada di urutan yang salah, header kolom berada di bawah hasil grep (ini tidak terjadi lagi), jadi saya mencoba untuk menukar perintah di sekitar:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

Hanya baris pertama yang dicetak, dan tidak ada yang lain! Bisakah saya menggunakan tee untuk mengarahkan ke grep, atau apakah saya melakukan ini dengan cara yang salah?

Ketika saya mengetik pertanyaan ini, perintah kedua sebenarnya bekerja sekali untuk saya, saya menjalankannya lagi lima kali dan kemudian kembali ke hasil satu baris. Apakah ini hanya sistem saya? (Saya menjalankan zsh dalam tmux).

Akhirnya, mengapa dengan perintah pertama adalah "grep syslog" tidak ditampilkan sebagai hasilnya (hanya ada satu hasil)?

Untuk kontrol di sini adalah grep tanpa tee

ps aux | grep syslog
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4
henry    2290  0.0  0.1  95220  3092 ?        Ssl  Sep07   3:12 /usr/bin/pulseaudio --start --log-target=syslog
henry   15924  0.0  0.0   3128   824 pts/4    S+   13:44   0:00 grep syslog

Pembaruan: Tampaknya head menyebabkan seluruh perintah terpotong (seperti ditunjukkan dalam jawaban di bawah) perintah di bawah ini sekarang mengembalikan yang berikut:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806
Rqomey
sumber
Bukan jawaban langsung untuk pertanyaan Anda tetapi akan jauh lebih bersih untuk melakukan sesuatu seperti itu ps aux | sed -n -e '1p' -e '/syslog/p'.
jw013
Saya bahkan tidak pernah memikirkan sed, saya pikir itu mungkin jawaban yang cocok untuk pertanyaan terkait di sini tapi saya sebenarnya mencari informasi tentang perilaku tidak konsisten dari perintah-perintah ini!
Rqomey

Jawaban:

19
$ ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND 
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

The grepdan headperintah mulai di sekitar waktu yang sama, dan keduanya menerima data input yang sama di waktu luang mereka sendiri, tetapi umumnya, sebagai data menjadi tersedia. Ada beberapa hal yang dapat memperkenalkan keluaran 'tidak disinkronkan' yang membalik baris; sebagai contoh:

  1. Data multiplexed dari teesebenarnya dikirim ke satu proses sebelum yang lain, terutama tergantung pada implementasi tee. teeImplementasi sederhana akan readsejumlah input, dan kemudian writedua kali: Sekali untuk stdout dan sekali untuk argumennya. Ini berarti bahwa salah satu tujuan tersebut akan mendapatkan data terlebih dahulu.

    Namun, semua pipa disangga. Kemungkinan buffer ini masing-masing adalah 1 baris, tetapi mereka mungkin lebih besar, yang dapat menyebabkan salah satu dari perintah penerima untuk melihat semua yang dibutuhkan untuk output (mis. grepJalur ped) sebelum perintah lainnya ( head) telah menerima data apa pun di semua.

  2. Terlepas dari hal di atas, ada juga kemungkinan bahwa salah satu dari perintah ini menerima data tetapi tidak dapat melakukan apa pun dengan waktu, dan kemudian perintah lainnya menerima lebih banyak data dan memprosesnya dengan cepat.

    Sebagai contoh, bahkan jika headdan grepdikirim data satu baris pada satu waktu, jika headtidak tahu bagaimana menghadapinya (atau tertunda oleh penjadwalan kernel), grepdapat menunjukkan hasilnya sebelum headbahkan mendapat kesempatan. Untuk menunjukkan, coba tambahkan penundaan: ps aux | tee >(sleep 1; head -n1) | grep syslogIni hampir pasti akan mengeluarkan grepoutput terlebih dahulu.

$ ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

Saya percaya Anda sering hanya mendapatkan satu baris di sini, karena headmenerima input baris pertama dan kemudian menutup stdin dan keluar. Ketika teemelihat bahwa stdout-nya telah ditutup, ia kemudian menutup stdin-nya sendiri (keluaran dari ps) dan keluar. Ini bisa tergantung pada implementasi.

Secara efektif, satu-satunya data yang psdapat dikirim adalah baris pertama (pasti, karena headmengendalikan ini), dan mungkin beberapa baris lain sebelum head& teemenutup deskriptor stdin mereka.

Ketidakkonsistenan dengan apakah baris kedua muncul diperkenalkan oleh pengaturan waktu: headmenutup stdin, tetapi psmasih mengirim data. Kedua peristiwa ini tidak disinkronkan dengan baik, sehingga baris yang berisi syslogmasih memiliki peluang untuk membuatnya ke teeargumen ( grepperintah). Ini mirip dengan penjelasan di atas.

Anda dapat menghindari masalah ini sama sekali dengan menggunakan perintah yang menunggu semua input sebelum menutup stdin / keluar. Misalnya, gunakan awkalih-alih head, yang akan membaca dan memproses semua barisnya (bahkan jika tidak ada keluaran):

ps aux | tee >(grep syslog) | awk 'NR == 1'

Tetapi perhatikan bahwa garis-garis tersebut masih dapat terlihat rusak, seperti di atas, yang dapat ditunjukkan oleh:

ps aux | tee >(grep syslog) | (sleep 1; awk 'NR == 1')

Semoga ini tidak terlalu detail, tetapi ada banyak hal simultan yang saling berinteraksi. Proses terpisah berjalan secara simultan tanpa sinkronisasi apa pun, sehingga tindakan mereka pada proses tertentu dapat bervariasi; terkadang membantu menggali jauh ke dalam proses yang mendasarinya untuk menjelaskan alasannya.

mrb
sumber
1
Jawaban Luar Biasa! Saya sebenarnya bertanya karena saya tertarik dengan proses yang mendasarinya. Ketika segala sesuatunya tidak konstan, saya menganggapnya menarik. Apakah akan ada cara yang lebih baik untuk menjalankan ps aux | tee >(grep syslog) | head -n1yang akan berhenti headmenutup stdout. Wow, perintah ini sudah mulai memberikan hasil sekarang, tetapi seperti yang akan terjadi sesuai dengan jawaban Anda, tampaknya akan terpotongUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND syslog 806
Rqomey
1
Anda dapat menggunakan sesuatu yang tidak menutup stdin bukan head. Saya telah memperbarui jawabannya dengan contoh ini:ps aux | tee >(grep syslog) | awk 'NR == 1'
mrb
1
@ KrzysztofAdamski, ketika Anda menggunakan >(cmd), shell membuat pipa bernama dan melewati itu sebagai argumen ke perintah ( tee). Kemudian teemenulis ke stdout (disalurkan ke awk) dan juga untuk argumen itu. Itu sama seperti mkfifo a_fifo ; grep ... a_fifodi satu shell dan ps | tee a_fifo | awk ...di yang lain.
mrb
1
@KrzysztofAdamski gnu.org/software/bash/manual/html_node/… - Coba echo >(exit 0), yang akan menggemakan argumen aktual yang diteruskan oleh shell (dalam kasus saya, menjadi /dev/fd/63). Ini harus bekerja sama pada bash dan zsh.
mrb
1
@ mrb: ini adalah fitur yang sangat menarik yang saya tidak tahu sebelumnya, terima kasih. Ini bekerja dengan cara yang aneh di bash, namun, lihat pastebin.com/xFgRcJdF . Sayangnya saya tidak punya waktu untuk menyelidiki ini sekarang tetapi saya akan melakukannya besok.
Krzysztof Adamski
2

grep syslogtidak selalu ditampilkan karena tergantung pada waktu. Saat menggunakan pipa shell, Anda menjalankan perintah hampir secara bersamaan. Tetapi kuncinya di sini adalah kata "hampir". Jika psselesai memindai semua proses sebelum grep diluncurkan, itu tidak akan ada dalam daftar. Anda bisa mendapatkan hasil acak tergantung pada beban sistem dll.

Hal serupa terjadi dengan tee Anda. Ini dijalankan pada latar belakang dalam subkulit dan dapat dipecat sebelum atau setelah grep. Inilah sebabnya mengapa urutan output tidak konsisten.

Sedangkan untuk pertanyaan tee, perilakunya cukup aneh. Ini karena tidak digunakan dengan cara normal. Ini dijalankan tanpa argumen yang berarti harus menyalin data dari stdin ke stdout. Tapi stdout itu diarahkan ke subshell running head (dalam kasus pertama) atau grep (kasus kedua). Tetapi itu juga disalurkan ke perintah berikutnya. Saya pikir apa yang terjadi dalam kasus ini sebenarnya tergantung pada implementasi. Misalnya pada bash 4.2.28 saya, tidak ada yang pernah ditulis untuk subshell stdin. Pada zsh, ini berfungsi andal seperti yang Anda inginkan (mencetak baris pertama ps dan baris yang dicari), setiap kali saya mencoba,

Krzysztof Adamski
sumber
Itu menjelaskan satu hal, saya terkejut bahwa tee keterlambatan berjalan sampai batas tertentu!
Rqomey
0

Sedikit retas, tapi ini solusinya, dalam bentuk psgrep()fungsi shell yang saya gunakan:

Arahkan ulang psbaris tajuk ke STDERR, lalu grepaktif STDOUT, tetapi pertama-tama hapus grepperintah itu sendiri, untuk menghindari baris "derau" yang berasal dari grepdirinya sendiri:

psgrep() { ps aux | tee >(head -1>&2) | grep -v " grep $@" | grep "$@" -i --color=auto; }
fnl
sumber