Bisakah zsh mengakses stdout program yang terakhir kali dijalankan?

14

Saya sering menggunakan findatau locateuntuk mencari tahu tentang jalan.

(~) locate foobar.mmpz
/home/progo/lmms/projects/foobar.mmpz

Langkah selanjutnya adalah membuka atau memanipulasi file. Dalam kasus bahagia seperti di atas, saya bisa melakukan ini:

(~) ls `!!`
ls `locate foobar.mmpz`
/home/progo/lmms/projects/foobar.mmpz

Tapi tidak ada yang terlalu senang ketika ada banyak jalur output, beberapa di antaranya mungkin bukan jalan atau hal lain semacam itu. Selain itu, menjalankan kembali perintah yang berpotensi boros juga tidak elegan.

Apakah akan ada cara untuk menghubungkan zsh untuk menyimpan stdout ke dalam array untuk manipulasi nanti? Lagi pula, tugas shell adalah untuk mengarahkan aliran ke pengguna. Saya pikir ini bisa menyimpan N pertama dan N baris terakhir dalam variabel untuk digunakan nanti segera, seperti $?dan lain-lain.

Ok jadi ini cukup keren: /unix//a/59704/5674 . Saya sekarang bertanya tentang zsh know-how (dan porting kode ke zsh) untuk memasang jenis penangkapan ini setelah setiap baris dijalankan.

unperson325680
sumber
Sebenarnya bukan tugas shell untuk mengalihkan aliran ke pengguna. Aplikasi menulis output mereka ke perangkat terminal, shell tidak terlibat di dalamnya.
Stéphane Chazelas
@StephaneChazelas, tetapi tugas shell adalah, misalnya, mengarahkan aliran ke file per permintaan pengguna. Ada juga resep untuk zsh yang stderr warna merah untuk memisahkannya dari stdout. Saya pikir itu menunjukkan peran shell dalam hal streaming.
unperson325680
Ya, Anda bisa melakukannya menggunakan screenatau scriptdan kait precmd dan preexec.
Stéphane Chazelas

Jawaban:

6

Tidak ada fitur untuk menangkap output dari layar pada kebanyakan emulator terminal. Saya ingat penulis xterm (emulator terminal "referensi") yang menyatakan bahwa akan sulit untuk diimplementasikan. Bahkan jika itu mungkin, shell harus melacak di mana prompt terakhir berada.

Jadi Anda tidak akan luput dari menjalankan perintah lagi, kecuali jika Anda menggunakan mekanisme manual spesifik terminal seperti menyalin-paste dengan mouse di xterm atau dengan keyboard di Layar.

Akan sangat tidak praktis bagi shell untuk secara otomatis menangkap output perintah, karena shell tidak dapat membedakan antara perintah yang memiliki terminal kompleks dan interaksi pengguna dari perintah yang hanya menampilkan karakter yang dapat dicetak.

Anda dapat menjalankan kembali perintah dan menangkap hasilnya. Ada berbagai cara untuk melakukannya. Untuk menjalankan kembali perintah, Anda dapat menggunakan:

  • !! substitusi sejarah - paling mudah diketik;
  • fc -e -, yang dapat digunakan dalam suatu fungsi.

Untuk menangkap output, Anda dapat menggunakan substitusi perintah, atau fungsi seperti berikut:

K () {
  lines=("${(f@)$(cat)}")
}
!! |K

Ini mengatur linesarray ke output dari perintah yang disalurkan ke dalamnya.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Jadilah itu. Saya sekarang menyadari dengan penjelasan yang baik bahwa pendekatan yang sepenuhnya otomatis terlalu sulit, hal terbaik kedua adalah sesuatu seperti Kmilik Anda.
unperson325680
3

Inilah potongan pertama dari sesuatu untuk menempatkan baris terakhir dari output dalam variabel yang disebut $lastline.

precmd () {                                                                                                                                                                                                        
    exec 2>&- >&-
    lastline=$(tail -1 ~/.command.out) 
    sleep 0.1   # TODO: synchronize better
    exec > /dev/tty 2>&1
}

preexec() {
    exec > >(tee ~/.command.out&)
}

Ini menggunakan preexeckait zsh untuk dijalankan execdengan teemenyimpan salinan stdout perintah, kemudian menggunakan precmduntuk membaca output yang disimpan dan mengembalikan stdout menjadi hanya terminal untuk menampilkan prompt.

Tetapi masih perlu beberapa pekerjaan. Misalnya, karena stdout bukan lagi terminal, program seperti vimdan lesstidak berfungsi dengan baik.

Ada beberapa informasi terkait yang bermanfaat dalam pertanyaan ini:

Mikel
sumber
Ya, mungkin pendekatan otomatis 100% tidak akan berhasil. Ada banyak execpanggilan dalam kode, apakah perintah berjalan beberapa kali atau saya terjebak dalam semantik khusus?
unperson325680
exectanpa nama program hanya menetapkan pengalihan.
Mikel
2

Anda dapat melakukan ini hanya dengan mem-piping hasil Anda ke tee, yang hanya menyimpan output ke file dan menampilkannya pada saat yang sama.

Jadi misalnya, Anda bisa melakukan ini, yang menunjukkan output Anda seperti biasa, tetapi juga menyimpannya ke file /tmp/it

locate foobar.mmpz | tee /tmp/it

kemudian cat file itu dan ambil untuk memilih sesuatu, mis

cat /tmp/it | grep progo | grep lms

lalu untuk menggunakannya, Anda bisa melakukan ini:

vi $(!!)

Taman Brad
sumber
2

Saya datang dengan solusi setengah matang ini:

alias -g ___='"$(eval "$(fc -ln -1)" | tail -n 1)"'

Ini memungkinkan Anda untuk menulis ___kapan saja di baris perintah. Perintah sebelumnya akan dijalankan kembali dan ___akan diganti dengan baris terakhir dari outputnya. Contoh penggunaan:

$ touch foo bar baz
$ ls -1
bar
baz
foo
$ vim ___

Perintah terakhir akan diperluas ke vim foo.

Ini memang memiliki beberapa tepi tajam!

  • Jika Anda memasukkan ___dalam perintah tetapi perintah sebelumnya juga termasuk a ___, shell akan bertahan dalam keadaan aneh untuk sementara waktu. Anda dapat segera keluar dari kondisi ini dengan Ctrl- C.

  • Anda juga tidak dapat menekan Tabuntuk memperluas ___, seperti yang Anda bisa dengan !$dan konstruksi lainnya.

  • Beberapa perintah akan menampilkan output yang berbeda ketika dijalankan "normal" dan ketika mereka terpasang ke pipa. (Bandingkan output dari lsdan ls | cat.) Jika perintah yang dipicu oleh ___salah satunya, Anda mungkin menjalankan perintah yang berbeda dari yang Anda harapkan.

  • Dan tentu saja, jika Anda ingin melakukan sesuatu dengan jalur output selain yang terakhir, ini tidak akan membantu Anda.

Saya memilih nama ___itu karena itu adalah sesuatu yang saya tidak pernah ingin sertakan sebagai kata dalam baris perintah, bahkan sebagai argumen. Anda dapat memilih nama yang berbeda, tetapi berhati-hatilah untuk tidak memilih sesuatu yang mungkin diperluas untuk Anda secara tidak sengaja.

bdesham
sumber