Bagaimana saya bisa mengeksekusi baris terakhir dari output di bash?

12

Saya tahu saya bisa menjalankan perintah terakhir di bash, dengan !!, tetapi bagaimana saya bisa menjalankan baris keluaran terakhir?

Saya sedang memikirkan use case dari output ini:

The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

Tetapi saya tidak tahu bagaimana saya bisa menjalankannya. Saya sedang memikirkan sesuatu seperti !!, mungkin @@atau serupa?


Pengguna Super juga memiliki pertanyaan ini.

Tim
sumber
1
Mengapa tidak Anda salin dan tempel saja?
Pilot6
1
@ Tim Anda ingin cara untuk menjalankan baris terakhir dari output program, tetapi untuk kasus penggunaan khusus ini, ada juga pendekatan untuk mengubah command_not_found_handlefungsi shell atau skrip yang dijalankannya (biasanya hanya /usr/lib/command-not-found). Saya pikir Anda bisa membuatnya sehingga Anda diminta secara interaktif dengan opsi untuk menginstal paket secara otomatis, atau (mungkin lebih baik) sehingga nama paket disimpan di suatu tempat dan dapat diinstal dengan menjalankan perintah singkat. Itu cukup berbeda dari yang Anda tanyakan di sini, Anda dapat mempertimbangkan untuk memposting pertanyaan terpisah tentang hal itu.
Eliah Kagan
2
Mungkin juga relevan: github.com/nvbn/thefuck (Abaikan nama) alat ini secara otomatis mengoreksi perintah git / apt / etc :)
bhathiya-perera

Jawaban:

16

Perintah $(!! |& tail -1)harus dilakukan:

$ git something
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

$ $(!! |& tail -1)
$(git something |& tail -1)
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
git-man liberror-perl

Seperti yang Anda lihat, sudo apt-get install gitperintah sedang berjalan.

EDIT: Hancur$(!! |& tail -1)

  • $()adalah bashpola substitusi perintah

  • bashakan memperluas !!ke perintah yang terakhir dieksekusi

  • |&bagian itu rumit. Biasanya pipa |akan mengambil STDOUT dari perintah sisi kiri dan meneruskannya sebagai STDIN ke perintah di sisi kanan |, dalam kasus Anda perintah sebelumnya mencetak hasilnya pada STDERR sebagai pesan kesalahan. Jadi, melakukan |tidak akan membantu, kita perlu meneruskan STDOUT dan STDERR (atau hanya STDERR) ke perintah sisi kanan. |&akan melewati STDOUT dan STDERR baik sebagai STDIN ke perintah sisi kanan. Sebagai alternatif, lebih baik Anda hanya dapat melewati STDERR:

    $(!! 2>&1 >/dev/null | tail -1)
  • tail -1seperti biasa akan mencetak baris terakhir dari inputnya. Di sini daripada mencetak baris terakhir Anda bisa lebih tepat seperti mencetak baris yang berisi apt-get installperintah:

    $(!! 2>&1 >/dev/null | grep 'apt-get install')
heemayl
sumber
1
Pendekatan Anda menganggap output akan sama untuk kedua kalinya. Beberapa perintah dapat menghasilkan output yang berbeda di waktu berikutnya, dalam hal ini sangat sulit untuk memprediksi apa yang mengeksekusi baris terakhir dari output yang sebenarnya akan dilakukan.
kasperd
1
@kasperd Anda benar..kesehatan sejauh menyangkut contoh spesifik ini, output harus tetap sama ..
heemayl
7

TL; DR: alias @@='$($(fc -ln -1) |& tail -1)'

Fasilitas interaksi sejarah Bash tidak menawarkan mekanisme untuk memeriksa output dari perintah. Shell tidak menyimpannya , dan ekspansi histori khusus untuk perintah yang Anda jalankan sendiri, atau bagian dari perintah itu.

Ini meninggalkan pendekatan rerunning perintah terakhir dan memipatkan stdout dan stderr ( |&) ke dalam substitusi perintah. Jawaban heemayl mencapai ini, tetapi tidak dapat digunakan dalam alias karena shell melakukan ekspansi sejarah sebelum memperluas alias, dan bukan setelahnya.

Saya tidak bisa mendapatkan ekspansi sejarah untuk berfungsi dalam fungsi shell, bahkan dengan mengaktifkannya di fungsi dengan set -H. Saya curiga !!dalam suatu fungsi tidak akan pernah diperluas, dan saya tidak yakin apa yang akan diperluas jika itu terjadi, tetapi saat ini saya tidak yakin persis mengapa tidak.

Oleh karena itu, jika Anda ingin mengatur hal-hal sehingga Anda dapat melakukan ini dengan mengetik sangat sedikit, Anda harus menggunakan fcshell builtin alih-alih ekspansi sejarah untuk mengekstrak perintah terakhir dari sejarah. Ini memiliki keuntungan tambahan yang berfungsi bahkan ketika ekspansi sejarah dinonaktifkan.

Seperti ditunjukkan dalam Gordon Davisson 's jawaban untuk Membuat alias mengandung ekspansi bash sejarah (pada Super User ), $(fc -ln -1)mensimulasikan !!. Memasukkan ini ke dalam !!dalam perintah heemayl $(!! |& tail -1) menghasilkan:

$($(fc -ln -1) |& tail -1)

Ini berfungsi seperti $(!! |& tail -1)tetapi dapat masuk dalam definisi alias:

alias @@='$($(fc -ln -1) |& tail -1)'

Setelah Anda menjalankan definisi itu, atau memasukkannya ke dalam .bash_aliasesatau .bashrcdan memulai shell baru, Anda cukup mengetik @@(atau apa pun yang Anda bernama alias) untuk mencoba untuk mengeksekusi baris terakhir dari output dari perintah terakhir.

ek@Io:~$ alias @@='$($(fc -ln -1) |& tail -1)'
ek@Io:~$ evolution
The program 'evolution' is currently not installed. You can install it by typing:
sudo apt-get install evolution
ek@Io:~$ @@
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  evolution-common evolution-data-server evolution-data-server-online-accounts
....
Eliah Kagan
sumber
Apakah ada opsi untuk memindahkan ini ke skrip terpisah? Saya mencobanya dan akhirnya gagal, karena fc tidak menghasilkan apa-apa ... Karena fc mengikuti sejarah sesi shell saat ini, jadi memindahkannya ke skrip terpisah membuka sesi berbeda di sana dengan riwayat kosong, tentu saja ... Saya menambahkan beberapa perintah di atas baris fc dalam skrip, dan salah satunya dieksekusi. Jadi, saya bertanya-tanya apakah ada opsi untuk memberitahu skrip, bahwa "konteksnya" adalah sesi bash, yang mengeksekusinya. Seperti, beberapa argumen untuk #! / Bin / bash atau sesuatu ...
Exterminator13
@ Exterminator13 Jika Anda maksud skrip terpisah yang Anda jalankan - seperti ./script, bukan bahwa Anda sumber dengan .atau sourcebuiltin - Saya tidak yakin. Bash menambahkan ke file saat keluar shell atau ketika Anda menjalankan historydengan -aatau -w(lihat help history). Jika ya, perintah dalam banyak shell interaktif akan disisipkan (muncul sesuai urutan Anda menjalankannya). Anda dapat segera menambahkannya. . Tapi saya pikir masih banyak yang harus dilakukan untuk membuat fcnaskah. Saya sarankan memposting pertanyaan baru tentang ini. Jika ya, silakan berkomentar di sini dengan tautan ke sana.
Eliah Kagan
2

Jika Anda menggunakan emulator terminal di bawah X, suka gnome-terminal, konsoleatau xterm, Anda dapat menggunakan pilihan X untuk sesuatu seperti salin dan tempel:

Gunakan pilihan utama

Pilih seluruh baris untuk dijalankan dengan klik kiri tiga kali lipat .

Kemudian tempel ke baris perintah menggunakan klik tombol tengah .

Ini harus bekerja di sebagian besar emulator terminal. Itu memanfaatkan pemilihan utama, tanpa menyentuh clipboard - mereka terpisah.
Memilih, dan kemudian menggabungkan salin dan tempel dalam satu operasi, secara teknis.

Volker Siegel
sumber
1
@Tim Itu benar - saya akan menjelaskannya.
Volker Siegel
1

Seperti yang Eliah Kagan sebutkan , shell tidak menyimpan output perintah. Namun, ada cara untuk mendapatkan baris terakhir output perintah terakhir, tanpa menjalankan perintah lagi , jika Anda menjalankan layar .

Masukkan baris berikut ke Anda ~/.screenrc:

register t "^a[k0y$ ^a]^a^L"
bind t process t

Kemudian Anda dapat menekan Ctrl+ atuntuk memasukkan baris sebelumnya di prompt saat ini. Mungkin untuk membuat ini juga secara otomatis tekan enter, tetapi mungkin ide yang lebih baik untuk memeriksanya sebelum Anda menjalankan dan cukup tekan enter secara manual.

Bagaimana itu bekerja:

  • register tmendaftarkan string ke buffer bernama t. Berikut string yang merupakan urutan kunci.
  • bind tmengikat ke kunci t(mis. mengeksekusi menggunakan Ctrl+ at), process tberarti mengevaluasi konten buffer bernama t.
  • Urutan itu sendiri berarti:
    • ^a[(= Ctrla[): masuk ke mode salin
    • knaik satu baris ke atas, 0ke awal baris, y$salin sampai akhir baris, (spasi) selesaikan perintah
    • ^a](= Ctrla]): rekatkan konten yang disalin
    • ^a^L(= CtrlaCtrlL) segarkan layar (jika tidak, konten yang di-paste tidak akan muncul, karena Anda tidak mengetiknya sendiri
atextor
sumber