Bagaimana cara menjalankan perintah shell secara diam-diam?

70

:!<command>dapat digunakan untuk menjalankan perintah di shell. Tapi ini "mengambil alih" terminal saya dan mengisinya dengan stdoutperintah khusus itu.

Bagaimana cara menjalankan perintah di latar belakang yang hanya memberi tahu saya tentang kode keluar yang tidak nol?

OrangeTux
sumber
1
Apakah Anda bersedia menggunakan antarmuka + python, atau salah satu antarmuka bahasa lainnya?
joeytwiddle
1
@ joeytwiddle Ya
OrangeTux

Jawaban:

52

:silent exec "!command"

Perhatikan bahwa sesi vim Anda masih akan ditempati saat perintah Anda dijalankan. Ini karena sifat sinkron Vim. Anda masih dapat kembali ke shell Anda dengan menekan CTRL + z (untuk mengirim Vim ke latar belakang) dan kemudian melanjutkan vim dengan perintah fgseperti biasa.

Untuk melakukan berbagai hal secara tidak sinkron, lihatlah plugin vim-dispatch Tim Pope atau proyek NeoVim yang memiliki dukungan asli untuk eksekusi perintah asinkron, yang dapat Anda manfaatkan dengan mudah menggunakan plugin NeoMake. Versi terbaru Vim juga memiliki dukungan untuk tugas yang tidak sinkron.

Lihat : h: diam

OliverUv
sumber
5
Ini juga hal pertama yang saya coba ketika saya melihat pertanyaan :-) Tetapi jika Anda menggunakan perintah yang output ke stdout (atau stderr), itu akan "menghancurkan" jendela Vim utama Anda (coba :silent !ls) ... Itu juga tidak dapat memberikan output yang tepat pada kode keluar non-0 ... Jadi saya khawatir ini sedikit lebih terlibat daripada hanya menggunakan :silent...
Martin Tournoij
Oh maaf. Menambahkan beberapa catatan tentang eksekusi asinkron saat Anda menulis komentar. Tidak tahu cara mengatasi masalah status keluar. Anda mungkin ingin mencoba #vim di Freenode.
OliverUv
Saya juga ingin mencatat bahwa tidak ada :silent exec "!ls"atau tidak :silent !lsmenampilkan output sama sekali pada versi saya Vim, 7.4 dengan tambalan 1-213.
OliverUv
3
Hm, inilah yang saya dapatkan :silent !ls: i.stack.imgur.com/1XvS6.png ... Saya perlu menekan ^Luntuk memperbaikinya lagi ...
Martin Tournoij
vim-dispatch tentu terlihat seperti solusi untuk masalah yang diberikan.
joeytwiddle
30

Untuk menjalankan perintah tanpa memicu pesan Enter, seperti:

Tekan ENTER atau ketik perintah untuk melanjutkan

coba contoh sederhana berikut ini:

:silent !echo Hello

Kemudian tekan Ctrl+ L(atau :redraw!) untuk menyegarkan layar saat kembali ke Vim.

Untuk menghindari perlunya penyegaran, Anda dapat menentukan perintah khusus Anda sendiri, seperti:

:command! -nargs=1 Silent execute ':silent !'.<q-args> | execute ':redraw!'

Sekarang, Anda dapat menggunakan perintah Vim baru untuk menjalankan perintah shell (catatan: tanpa ! Dan dengan huruf kapital S ):

:Silent ps

Sumber: Menghindari permintaan "Hit ENTER to continue" di situs Vim Wikia

kenorb
sumber
1
Bagaimana Anda mendapat pemberitahuan tentang kode keluar yang tidak nol?
Martin Tournoij
1
Bukan solusi untuk kode keluar non-nol, tetapi untuk menambah ini, perintah Diam ini memungkinkan perintah ingin :Silent ctags -R . > /dev/null &dijalankan di latar belakang. Mengirim stdout ke / dev / null mencegah output muncul di buffer vim.
ftvs
10

Solusi cepat dan kotor (dalam Vimscript murni)

Ini dapat memulai proses di latar belakang:

:!slow_command_here > /tmp/output 2>&1 &

Tetapi Vim membutuhkan cara untuk mengetahui kapan prosesnya telah selesai, dan bagaimana, jadi mari kita gunakan file marker:

:!(rm -f /tmp/finished; slow_command_here > /tmp/output 2>&1; echo "$?" > /tmp/finished) &

Sekarang kita dapat meminta Vim untuk memeriksa sesering mungkin apakah proses telah selesai:

:augroup WaitForCompletion
:autocmd!
:autocmd CursorHold * if filereadable('/tmp/finished') | exec "augroup WaitForCompletion" | exec "au!" | exec "augroup END" | echo "Process completed with exit code ".readfile('/tmp/finished')[0] | end
:augroup END

Hei itu tidak cantik, tapi itu semacam bekerja!

Kekurangan

Sayangnya autocmd di atas tidak akan memicu sampai Anda memindahkan kursor. Dan jika Anda ingin menjalankan lebih dari satu proses latar belakang sekaligus, Anda perlu menambahkan ID unik ke file-file itu, dan ke nama grup autocmd.

Jadi, jika Anda dapat menangani ketergantungan ekstra, Anda mungkin lebih baik menggunakan solusi yang telah dicoba dan diuji seperti plugin vim-dispatch yang disebutkan dalam jawaban lain.

Menampilkan output

Jika Anda ingin melihat output dari proses, bukan hanya kode keluar , ganti final di echoatas dengan:

silent botright pedit /tmp/output

Itu akan membuka jendela pratinjau. Untuk menggunakan daftar kesalahan perbaikan cepat sebagai gantinya:

silent cget /tmp/output | copen

Daftar perbaikan cepat memungkinkan Anda dengan mudah menavigasi ke kesalahan menggunakan :cnext. Namun copenmemindahkan kursor Anda ke jendela perbaikan cepat ketika terbuka, yang mungkin akan mengejutkan / mengganggu.

(Solusi untuk itu adalah dengan membuka jendela QF dengan :copenketika awalnya memulai proses, jadi Anda tidak perlu menyebutnya di akhir.)

joeytwiddle
sumber
2
Ini adalah satu-satunya jawaban di sini yang benar-benar menjawab pertanyaan: "jalankan perintah di latar belakang yang hanya memberi tahu saya pada kode keluar yang tidak nol" ... Saya tidak tahu mengapa jawaban lain bahkan memiliki upvotes sama sekali ...
Martin Tournoij
2
Saya percaya mereka memiliki masalah karena mereka benar-benar menjawab judul pertanyaan, yang membawa pengunjung ke halaman ini. "Bagaimana cara menjalankan perintah shell secara diam-diam?" Fenomena SE umum! Pengunjung bersyukur, tetapi mungkin tidak disiplin.
joeytwiddle
Di dunia yang ideal, kita mungkin memiliki dua pertanyaan terpisah: "Bagaimana cara menjalankan perintah shell secara diam-diam?" dan "Bagaimana menjalankan perintah shell di latar belakang?" sehingga googler dapat menemukan apa yang mereka cari.
joeytwiddle
6

Menjalankan perintah di latar belakang

Saya menjalankan perintah yang sedikit diblokir, dan saya tidak terlalu peduli dengan hasilnya. Ini dapat diatasi dengan memulai proses yang dilampirkan bukan ke terminal / emulator melainkan ke sistem dan mengarahkan semua output ke / dev / null. Sebagai contoh:

:silent exec "!(espeak 'speaking in background'&) > /dev/null"

yang (...&)berjalan di latar belakang dan > /dev/nullpengalihan semua output ke /dev/null(tidak).

Tanda kurung menjebak output ke dalam subkulit (atau sesuatu seperti itu), tetapi memiliki efek samping karena tidak melekat pada shell saat ini (bukan masalah besar).

Menjalankan perintah secara diam-diam di latar belakang dengan pemetaan

Aku baru sadar bahwa jika Anda berada , pada kenyataannya, pemetaan itu, Anda dapat melakukan sesuatu seperti

nnoremap <silent> <leader>e :!$(espeak 'speaking in the background'&) > /dev/null

Di atas tidak akan menampilkan apa pun di bilah perintah dan juga tidak akan menampilkan apa pun di terminal di luar vim. Ini akan dipetakan ke <leader> e, yang secara \ edefault.

Menjalankan perintah, menangkap outputnya, menampilkan isinya

(Suntingan lain - dan mungkin yang paling rapi). Satu ini akan menampilkan yang keluaran dari perintah jika Anda memilih demikian:


DALAM TAB BARU:

silent exec "!(echo 'hello. I'm a process! :)') > /tmp/vim_process" | :tabedit /tmp/vim_process

DI DALAM LAMARAN VERTIKAL:

silent exec "!(echo 'hello. I'm a process :)') > /tmp/vim_process" | :vs /tmp/vim_process

DI DALAM KECEPATAN HORIZONTAL:

silent exec "!(echo 'hello. I'm a process :)') > /tmp/vim_process" | :sp /tmp/vim_process

... melakukan apapun yang Anda inginkan

dylnmc
sumber
5

Jika Anda tidak peduli dengan kode keluar, Anda bisa menggunakan ini:

:call system('/usr/bin/zathura using-docker.pdf &')
hsnotebook
sumber
1
Jika Anda peduli dengan kode keluar, v:shell_errorvariabel akan memberi tahu Anda
Jerome Dalbert
4

Tidak yakin apakah ini sesuai dengan kebutuhan Anda, (tidak menangani proses latar belakang seperti dalam foo & - tidak yakin apakah itu yang Anda maksud dengan "di latar belakang" ), tetapi perintah khusus dapat digunakan seperti pada:

fun! s:PraeceptumTacet(cmd)
    silent let f = systemlist(a:cmd)
    if v:shell_error
        echohl Error
        echom "ERROR #" . v:shell_error
        echohl WarningMsg
        for e in f
            echom e
        endfor
        echohl None
    endif
endfun

command! -nargs=+ PT call s:PraeceptumTacet(<q-args>)

Kemudian gunakan sebagai contoh:

:PT ls -l
:PT foobar
ERROR #127
/bin/bash: foobar: command not found

Jika Anda tidak perlu / ingin pesan kesalahan yang cukup sederhana system(), maka periksa v:shell_errordan laporkan dengan misalnya echo Error!.

Dari bantuan v: shell_error :

                                        v:shell_error shell_error-variable
v:shell_error   Result of the last shell command.  When non-zero, the last
                shell command had an error.  When zero, there was no problem.
                This only works when the shell returns the error code to Vim.
                The value -1 is often used when the command could not be
                executed.  Read-only.
                Example: >
    :!mv foo bar
    :if v:shell_error
    :  echo 'could not rename "foo" to "bar"!'
    :endif
                "shell_error" also works, for backwards compatibility.
Runium
sumber
Ini tidak benar-benar menjalankannya di latar belakang; Anda masih harus menunggu sampai selesai.
Martin Tournoij
@Carpetsmoker: Ya, jadi komentar saya "... tidak menangani proses latar belakang seperti pada foo &..." . Dari teks Q secara keseluruhan saya menafsirkannya sebagai salah satu atau. Baik "Jalankan sebagai latar belakang perintah eksternal AKA" atau "Jalankan sebagai perintah eksternal _and_ sebagai proses latar belakang." . Saya menjawab terutama berdasarkan judul Q dll. - dan menambahkan komentar pada "Tidak yakin ..." - dan menyerahkannya ke OP untuk mengklarifikasi apakah ada atau.
Runium
3

Vim 8 memperkenalkan dukungan pekerjaan. Seseorang dapat menjalankan perintah eksternal di latar belakang tanpa bergantung pada plugin. Misalnya, untuk menjalankan server markdown ( markserv ) di lokasi saat ini dan tidak memblokir sesi vim:

:call job_start('markserv .')

Ini memulai proses markserv sebagai sub-proses dari proses vim saat ini. Anda dapat memverifikasi ini dengan pstree.

Lihat job_start

KFL
sumber
2

Saya menggunakan kode berikut:

command! -nargs=1 Silent call SilentExec(<q-args>)

function! SilentExec(cmd)
  let cmd = substitute(a:cmd, '^!', '', '')
  let cmd = substitute(cmd, '%', shellescape(expand('%')), '')
  call system(cmd)
endfunction

Sekarang Anda bisa mengetik yang berikut ini

:Silent !your_command

Ini hampir seperti silent !perintah bawaan Vim , kecuali untuk ibukota S. Bahkan memungkinkan untuk opsional !untuk membuatnya terlihat lebih mirip. Panggilan untuk system()membuat perintah shell benar-benar diam: tidak ada layar berkedip dan tidak ada gambar ulang.

(dan jika Anda memerlukan kode status, Anda dapat memeriksa v:shell_errorvariabel, lihat bantuan untuk info lebih lanjut)

Jerome Dalbert
sumber
1

The AsyncRun Plugin jika dirancang untuk ini, memungkinkan Anda untuk menjalankan perintah shell di latar belakang di Vim8 / NeoVim dan menampilkan output di jendela quickfix.

Sama seperti penggantian !perintah:

:AsyncRun ls -la /

Anda juga dapat menyembunyikan output di jendela perbaikan cepat sepenuhnya:

:AsyncRun -mode=3 ls -la /

Saat pekerjaan selesai, AsyncRun akan memberi tahu Anda dengan memberikan opsi -post:

:AsyncRun -post=some_vim_script   ls -la /

Script vim yang ditentukan oleh -postakan dieksekusi setelah pekerjaan selesai.

skywind3000
sumber