Terminal borked setelah menjalankan Vim dengan xargs

30

Saya terkadang mencoba memanggil Vim menggunakan xargs, seperti ini:

find . -name '*.java' | xargs vim

... jenis pekerjaan apa:

  1. Saat Vim diluncurkan, saya melihat lampu kilat peringatan berikut:

    Vim: Warning: Input is not from a terminal
    
  2. Pengeditan berfungsi - :filesdengan benar menyebutkan semua .javafile seperti yang diharapkan.
  3. Saya bisa menyimpan dan berhenti.

Namun, setelah keluar dari Vim, terminal saya borked:

  • Apa pun yang saya ketik di shell prompt tidak digaungkan.
  • Pengembalian kereta tidak muncul sama sekali, dan umpan baris hanya kadang-kadang muncul.

Ini berlangsung sampai saya mengeluarkan reset(1)perintah untuk menginisialisasi ulang terminal.

Apakah ini bug Vim, atau adakah penjelasan yang lebih memuaskan mengapa ia berinteraksi dengan terminal seperti itu? Saya telah melihatnya terjadi pada Vim hingga versi 7.3 (versi ini tampaknya tidak penting) di Linux dan berbagai Unix.

Saya menyadari satu solusi, yaitu vim $(find . -name '*.java'). Penanganan lainnya akan disambut baik, meskipun itu bukan pertanyaan utama saya.

200_sukses
sumber
Saya agak mendapatkan keinginan Anda untuk mengisi situs ini dengan sebanyak mungkin pertanyaan tetapi ... pertanyaan itu telah dijawab puluhan kali selama bertahun-tahun di SO / SU dan di tempat lain: xargsmenggunakan boneka stdinyang tidak dapat digunakan oleh Vim dan istirahat semuanya sesudahnya.
romainl
1
@romainl Saya tidak aktif di situs-situs itu, dan saya belum pernah melihatnya di sana - jujur.
200_sukses
Terkait: superuser.com/questions/336016/... - mungkin seseorang dapat menyingkat jawaban atas untuk jawaban yang bagus.
muru
2
Pertanyaan bagus - ini telah terjadi pada saya beberapa kali. Juga, saya tidak menggunakan situs SO lainnya - jika terkait dengan vim, saya ingin menemukannya di sini.
craigp

Jawaban:

21

Ini terjadi ketika vim dipanggil dan terhubung ke output pipa sebelumnya, bukan terminal dan menerima input yang berbeda yang tidak terduga (seperti NUL). Hal yang sama terjadi ketika Anda menjalankan :, vim < /dev/nulljadi resetperintah dalam hal ini membantu. Ini dijelaskan dengan baik oleh grawity di superuser .

Jika Anda menggunakan finduntuk menyampaikan nama file untuk diedit, Anda tidak perlu xargs, cukup gunakan -exec, misalnya:

find . -name '*.java' -exec vim {} +

Jika Anda ingin menggunakan xargs, pada Unix / macOS Anda harus menggunakan -oparameter, seperti:

find . -name '*.java' | xargs -o vim

-oBuka kembali stdin sebagai / dev / tty dalam proses anak sebelum menjalankan perintah. Ini berguna jika Anda ingin xargs menjalankan aplikasi interaktif.

atau:

find . -name "*.java" -type f -print0 | xargs -o -0 vim

Catatan: Menggunakan -print0/-0 akan mendukung nama file dengan spasi putih.


find + BSDxargs

Pada Unix / macOS Anda dapat mencoba solusi berikut:

find . -name '*.java' | xargs -J% sh -c 'vim < /dev/tty $@'

Anda juga dapat menggunakan sintaks substitusi perintah , misalnya:

vim $(find . -name '*.java')
vim `find . -name '*.java`

Atau gunakan GNU parallelalih-alih xargsuntuk memaksa alokasi tty, misalnya:

find . -name '*.java' | parallel -X --tty vi

catatan: parallel pada Unix / OSX tidak akan berfungsi karena memiliki parameter yang berbeda dan tidak mendukung tty.

Banyak perintah populer lainnya menyediakan alokasi pseudo-tty juga (seperti -t dalam ssh), jadi periksa bantuan.

Saran lain adalah menggunakan:

Terkait:

kenorb
sumber
GNU saya xargstidak punya -J. Itu tampaknya menjadi opsi MacOS (BSD) yang tidak ada dalam versi GNU.
Dijeda sampai pemberitahuan lebih lanjut.
12

Saran solusi: gunakan buffer sebagai navigator sistem file

Gunakan vim -perintah untuk membaca daftar jalur dari stdin. Vim :help --menjelaskan ini: 1

Mulai mengedit buffer baru, yang diisi dengan teks yang dibaca dari stdin. Perintah yang biasanya dibaca dari stdin sekarang akan dibaca dari stderr. Contoh:

find . -name "*.c" -print | vim -

Anda kemudian dapat menggunakan gfatauCtrl-wCtrl-f untuk menavigasi ke file dalam buffer yang sama atau di jendela split baru masing-masing.

Saran penyelesaian: daftar argumen

Cara hebat lainnya adalah dengan menggunakan daftar argumen Vim. The :argadd **/*.javaperintah akan mengisi daftar argumen Vim dengan semua file java yang ditemukan di dalam direktori saat ini secara rekursif. Anda kemudian dapat menggunakan :nextdan :prevuntuk berpindah di antara file.


1 Yang pertama -memberi tahu Vim bahwa Anda ingin mencari bantuan untuk opsi baris perintah, yang kedua sebenarnya merupakan flag baris perintah yang Anda cari. Baca :help help-contextlebih banyak trik seperti ini.

akshay
sumber
8

Selain itu reset, Anda dapat mencoba:

stty sane

yang seharusnya juga membuat terminal Anda dapat digunakan kembali.

Lihat di sini untuk penjelasan. Dan entah bagaimana ini dapat dianggap sebagai perilaku vim, setidaknya Neovim tidak memiliki masalah ini saat ini.

Ryenus
sumber
Ini tidak persis menjawab pertanyaan, tetapi itu membantu saya, jadi +1.
Pasang kembali Monica iamnotmaynard
Ini TIDAK menjawab pertanyaan saya;) Meskipun setelah membaca OP lebih saya kira resetjuga harus.
Sridhar Sarnobat
1

Alasannya adalah bahwa xargsset stdinke /dev/null, sedangkanvim kebutuhan stdinuntuk menjadi /dev/tty.


BSD xargsSolusi (misalnya Mac):

echo -e 'file1\nfile2' | xargs -o vim

-o mengatur stdin proses anak xarg ( vimdalam hal ini) menjadi dev/tty.


GNU xargsSolusi (misalnya Linux):

GNU xargstidak memiliki-o opsi. Sebaliknya, Anda harus menggunakan solusi yang lebih rumit. (Catatan: Sangat penting untuk memiliki zerostring tambahan, jangan lupakan itu.)

echo -e 'file1\nfile2' | xargs bash -c '</dev/tty vim "$@"' zero

Anda juga dapat menjadikannya sebagai alias:

alias vimin='xargs bash -c '\''</dev/tty vim "$@"'\'' zero'
echo -e 'file1\nfile2' | vimin

Penjelasan terperinci tentang solusi GNU xargs

Mari kita jabarkan langkah demi langkah:

echo -e 'file1\nfile2' | xargs bash -c '</dev/tty vim "$@"' zero

1. xargscukup tambahkan stdinsampai akhir string, sehingga xargsakan mengeksekusi ini:

    bash -c '</dev/tty vim "$@"' zero file1 file2

2. Format untuk bash -cis bash -c 'COMMAND_STRING' $0 $1 $2 etc.

"$@"memperluas ke parameter posisi "$1", "$2", dll. Ini tidak termasuk "$ 0" karena itu adalah parameter khusus untuk nama skrip, bukan parameter posisional. Inilah sebabnya mengapa kita perlu menambahkan string dummy zero(bisa berupa string apa saja) untuk menggantikannya $0. Jika tidak, Anda akan kehilangan file pertama.

Jadi setelah Anda berkembang "$@", Anda berakhir dengan:

    bash -c '</dev/tty vim file1 file2' 

3. bash -cakan menjalankan COMMAND_STRING:

    </dev/tty vim file1 file2 

</dev/ttymengatur stdinagar /dev/ttysehingga vimdapat bekerja dalam mode interaktif.

Wisbucky
sumber
+1 Ini sepertinya merupakan duplikat dari jawaban dengan jumlah suara terbanyak, tetapi tidak. The $0placeholder (di sini "nol") adalah kuncinya.
Dijeda sampai pemberitahuan lebih lanjut.
0

Saya menemukan semua jawaban ini kurang. Setelah berjuang melalui masalah xargs, cara yang paling sederhana - bagi saya! - untuk melakukan pencarian-dan-buka pada kotak generik adalah sebagai berikut:

vim -O `grep -lir mySearchTerm *`

Saya tidak punya masalah menggunakan finddengan xargsdangrep , tapi saya menemukan sintaks menjengkelkan. Dan sebagai coder harian yang menggunakan vimterminal sebagai IDE mereka, dan mencari file untuk properti secara konstan, inilah yang telah saya gunakan.

Ada waktu dan tempat untuk find . -path ./node_modules -prune -o -name '*.js' | xargs grep -i mySearchTerm digabungkan dengan membuka file melalui vim, dan metode lainnya. Bagi saya, itu jarang.

Semoga ini bisa membantu seseorang.

Gavin
sumber
2
FWIW disarankan untuk tidak menggunakan notasi backticks sebelumnya dan menggunakannya $(...). Tidak penting dalam baris perintah Anda seperti dalam skrip tetapi saya pikir masih lebih baik untuk menggunakannya dalam jawaban Anda :) (referensi di sini dan di sini )
statox