Jak cicho wykonywać polecenia powłoki?

70

:!<command>może być użyty do wykonania polecenia w powłoce. Ale to „przejmuje” mój terminal i wypełnia go stdouttym konkretnym poleceniem.

Jak wykonać polecenie w tle, które powiadamia mnie tylko o niezerowym kodzie wyjścia?

OrangeTux
źródło
1
Czy byłbyś skłonny użyć interfejsu + python lub jednego z innych interfejsów językowych?
joeytwiddle
1
@joeytwiddle Tak
OrangeTux

Odpowiedzi:

52

:silent exec "!command"

Zauważ, że twoja sesja vim nadal będzie zajęta podczas wykonywania polecenia. Wynika to z synchronicznej natury Vima. Nadal możesz wrócić do powłoki, naciskając CTRL + Z (aby wysłać Vima do tła), a następnie wznowić vim za pomocą polecenia, fgjak zwykle.

Aby zrobić wszystko asynchronicznie, spójrz na wtyczkę Tima Pope'a vim-dispatch lub projekt NeoVim, który ma natywne wsparcie dla wykonywania poleceń asynchronicznych, które możesz łatwo wykorzystać za pomocą wtyczki NeoMake. Najnowsza wersja Vima obsługuje także zadania asynchroniczne.

Zobacz : h: cichy

OliverUv
źródło
5
Jest to również pierwsza rzecz, którą próbowałem, kiedy zobaczyłem pytanie :-) Ale jeśli użyjesz polecenia, które wypisuje na stdout (lub stderr), spowoduje to „zablokowanie” twojego głównego okna Vima (próba :silent !ls) ... nie daje prawidłowego wyniku dla kodu wyjścia innego niż 0 ... Więc obawiam się, że jest to trochę bardziej zaangażowane niż tylko użycie :silent...
Martin Tournoij
Przepraszam. Dodano kilka uwag na temat wykonywania asynchronicznego podczas pisania komentarza. Nie wiem, jak rozwiązać problem z wyjściem. Możesz spróbować w #vim na Freenode.
OliverUv
Chcę również zauważyć, że ani :silent exec "!ls"nie :silent !lspokazuję żadnych wyników w mojej wersji Vima, 7.4 z łatkami 1-213.
OliverUv
3
Hm, oto co otrzymuję po :silent !ls: i.stack.imgur.com/1XvS6.png ... Muszę nacisnąć, ^Laby to naprawić ponownie ...
Martin Tournoij
vim-dispatch z pewnością wygląda jak rozwiązanie danego problemu.
joeytwiddle
30

Aby wykonać polecenie bez wyzwalania komunikatu Enter, na przykład:

Naciśnij ENTER lub wpisz polecenie, aby kontynuować

spróbuj następującego prostego przykładu:

:silent !echo Hello

Następnie naciśnij Ctrl+ L(lub :redraw!), aby odświeżyć ekran po powrocie do Vima.

Aby uniknąć potrzeby odświeżania, możesz zdefiniować własne niestandardowe polecenie, takie jak:

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

Teraz możesz użyć nowego polecenia Vima, aby uruchomić polecenie powłoki (uwaga: bez ! I dużymi literami S ):

:Silent ps

Źródło: Unikanie monitów „Hit ENTER, by kontynuować” na stronie Vim Wikia

kenorb
źródło
1
Jak otrzymujesz powiadomienia o niezerowym kodzie wyjścia?
Martin Tournoij,
1
Nie jest to rozwiązanie dla niezerowego kodu wyjścia, ale aby dodać do tego, to polecenie Silent pozwala :Silent ctags -R . > /dev/null &na uruchamianie poleceń w tle. Wysłanie standardowego wyjścia do / dev / null zapobiega wyświetlaniu danych wyjściowych w buforze vim.
ftvs
10

Szybkie i brudne rozwiązanie (w czystym Vimscript)

Może to uruchomić proces w tle:

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

Ale Vim potrzebuje sposobu, aby dowiedzieć się, kiedy proces się zakończy i jak, więc użyjmy pliku znaczników:

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

Teraz możemy prosić Vima, aby sprawdzał co jakiś czas, czy proces się zakończył:

: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

Hej, to nie jest ładne, ale to trochę działa!

Niedogodności

Niestety powyższy autocmd nie uruchomi się, dopóki nie przesuniesz kursora. A jeśli chcesz uruchomić więcej niż jeden proces w tle, musisz dodać unikalne identyfikatory do tych plików i do nazwy grupy autocmd.

Jeśli więc poradzisz sobie z dodatkową zależnością, lepiej byłoby użyć sprawdzonego rozwiązania, takiego jak wtyczka vim-dispatch wspomniana w innej odpowiedzi.

Wyświetlanie wyników

Jeśli chcesz zobaczyć wynik procesu, a nie tylko kod zakończenia , zamień ostatni echoz powyższych na:

silent botright pedit /tmp/output

To otworzy okno podglądu. Aby zamiast tego użyć listy błędów szybkiej poprawki:

silent cget /tmp/output | copen

Lista poprawek pozwala łatwo nawigować do błędów za pomocą :cnext. Jednak copenprzesuwa kursor do okna szybkiego dostępu, gdy zostanie otwarte, co prawdopodobnie będzie zaskakujące / denerwujące.

(Obejściem tego problemu byłoby otwarcie okna QF :copenprzy początkowym uruchomieniu procesu, więc nie trzeba będzie wywoływać go na końcu.)

joeytwiddle
źródło
2
To jest jedyna odpowiedź, która faktycznie odpowiada na pytanie: „wykonaj polecenie w tle, które powiadamia mnie tylko o niezerowym kodzie wyjścia” ... Nie mam pojęcia, dlaczego inne odpowiedzi w ogóle mają głos pozytywny ...
Martin Tournoij,
2
Wierzę, że mają pozytywne opinie, ponieważ doskonale odpowiadają na tytuł pytania, co przyciąga odwiedzających na tę stronę. „Jak cicho wykonywać polecenia powłoki?” Wspólne zjawisko SE! Odwiedzający są wdzięczni, ale być może nie zdyscyplinowani.
joeytwiddle 12.04.16
W idealnym świecie prawdopodobnie mielibyśmy dwa osobne pytania: „Jak cicho wykonywać polecenia powłoki?” i „Jak wykonywać polecenia powłoki w tle?” aby pracownicy Google mogli znaleźć to, czego szukają.
joeytwiddle 12.04.16
6

Uruchamianie polecenia w tle

Uruchomiłem polecenie, które zablokowało się na chwilę, i tak naprawdę nie obchodziło mnie wyjście. Można temu zaradzić, uruchamiając proces podłączony nie do terminala / emulatora, lecz do systemu i przekierowując wszystkie dane wyjściowe do / dev / null. Na przykład:

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

(...&)uruchamia go w tle i > /dev/nullprzekierowuje wszystkie dane wyjściowe do /dev/null(nic).

Nawias przechwytuje dane wyjściowe do podpowłoki (lub czegoś w tym rodzaju), ale ma taki efekt uboczny, że nie jest dołączony do bieżącej powłoki (nic wielkiego).

Ciche wykonywanie polecenia w tle za pomocą mapowania

Właśnie zdałem sobie sprawę, że jeśli w rzeczywistości, przyporządkowując je, można zrobić coś takiego

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

Powyższe nie wyświetli nic na pasku poleceń i dodatkowo nie wyświetli niczego w terminalu poza vimem. Zostanie on zamapowany <leader> e, co jest \ edomyślnie.

Uruchamianie polecenia, przechwytywanie jego wyniku i wyświetlanie jego zawartości

(Kolejna edycja - i może najładniejsza). Ten będzie wyświetlany na wyjście komendy jeśli więc wybrać:


WEWNĄTRZ NOWEJ ZAKŁADKI:

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

WEWNĄTRZ PIONOWEGO ROZDZIELENIA:

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

WEWNĄTRZ HORYZONTALNEGO ROZDZIELENIA:

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

... rób co chcesz

dylnmc
źródło
5

Jeśli nie obchodzi Cię kod wyjścia, możesz przejść z tym:

:call system('/usr/bin/zathura using-docker.pdf &')
hsnotebook
źródło
1
Jeśli zależy ci na kodzie wyjścia, v:shell_errorzmienna powie ci
Jerome Dalbert
4

Nie jestem pewien, czy odpowiada to twoim potrzebom (nie obsługuje procesów w tle jak w foo & - nie jestem pewien, czy to właśnie rozumiesz przez „w tle” ), ale niestandardowe polecenie może być użyte jak w:

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>)

Następnie użyj jako np .:

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

Jeśli nie potrzebujesz / nie chcesz, aby komunikat o błędzie system()wystarczył, wystarczy sprawdzić v:shell_errori zgłoś np echo Error!.

Z pomocy 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
źródło
To tak naprawdę nie działa w tle; wciąż musisz poczekać, aż się skończy.
Martin Tournoij,
@Carpetsmoker: Tak, dlatego mój komentarz „… nie obsługuje procesów w tle, jak w foo &…” . Z całego tekstu Q zinterpretowałem go jako albo. Albo „Wykonaj jako zewnętrzne polecenie AKA w tle” lub „Wykonaj jako zewnętrzne polecenie _ i_ jako proces w tle” . Odpowiedziałem głównie na podstawie tytułu Q itp. - dodałem komentarz do „Nie jestem pewien…” - i zostawiłem OP, aby wyjaśnić, czy któryś lub.
Runium,
3

Vim 8 wprowadził obsługę miejsc pracy. Można uruchamiać zewnętrzne polecenia w tle, nie polegając na wtyczkach. Na przykład, aby uruchomić serwer Markdown ( markserv ) w bieżącej lokalizacji i nie blokować sesji vim:

:call job_start('markserv .')

To uruchamia proces markserv jako podproces bieżącego procesu vim. Możesz to zweryfikować za pomocą pstree.

Zobacz job_start

KFL
źródło
2

Używam następującego kodu:

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

Teraz możesz wpisać następujące

:Silent !your_command

Wygląda to prawie jak wbudowane silent !polecenie Vima , z wyjątkiem stolicy S. Pozwala nawet na opcjonalne, !aby wyglądał jeszcze bardziej podobnie. Wezwanie do system()tego, aby polecenie powłoki było naprawdę ciche: żaden ekran nie miga i nie przerysowuje.

(a jeśli kiedykolwiek potrzebujesz kodu stanu, możesz sprawdzić v:shell_errorzmienną, zobacz pomoc, aby uzyskać więcej informacji)

Jerome Dalbert
źródło
1

AsyncRun wtyczki jeśli przeznaczone do tego, że pozwala na uruchomienie poleceń powłoki w tle w Vim8 / NeoVim i wyświetla dane wyjściowe w quickfix okna.

Podobnie jak zamiana !polecenia:

:AsyncRun ls -la /

Możesz również całkowicie ukryć dane wyjściowe w oknie szybkiej poprawki:

:AsyncRun -mode=3 ls -la /

Po zakończeniu zadania AsyncRun powiadomi Cię, przekazując opcję -post:

:AsyncRun -post=some_vim_script   ls -la /

Zdefiniowany przez skrypt vim -postzostanie wykonany po zakończeniu zadania.

skywind3000
źródło