Terminale interrotto dopo aver richiamato Vim con xargs


30

A volte ho provato a invocare Vim usando xargs, in questo modo:

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

... che tipo di lavori:

  1. All'avvio di Vim, vedo brevemente il seguente avviso lampeggiante:

    Vim: Warning: Input is not from a terminal
    
  2. La modifica funziona: :fileselenca correttamente tutti i .javafile come previsto.
  3. Posso salvare ed uscire.

Tuttavia, dopo essere uscito da Vim, il mio terminale è bloccato:

  • Qualunque cosa scrivo al prompt della shell non viene ripetuta.
  • I ritorni a capo non compaiono affatto e gli avanzamenti di riga vengono visualizzati solo a volte.

Questo continua fino a quando non invio un reset(1)comando per reinizializzare il terminale.

È un bug di Vim o c'è una spiegazione più soddisfacente del perché interagisce con il terminale in quel modo? L'ho visto accadere su Vim fino alla versione 7.3 (la versione non sembra avere importanza) su Linux e vari Unices.

Sono a conoscenza di una soluzione alternativa, vale a dire vim $(find . -name '*.java'). Altre soluzioni alternative sarebbero le benvenute, sebbene questa non sia la mia domanda principale.


Ho il desiderio di riempire questo sito con quante più domande possibili ma ... a quella domanda è stata data risposta dozzine di volte negli anni su SO / SU e altrove: xargsusa un manichino stdinche non può essere usato da Vim e si rompe tutto dopo.
romainl,

1
@romainl Non sono attivo su quei siti e non l'ho mai visto chiedere lì - onesto.
200_successo

Correlati: superuser.com/questions/336016/… - forse qualcuno può condensare le risposte migliori a una risposta eccellente.
Muru,

2
Ottima domanda: questo mi è successo molte volte. Inoltre, non utilizzo altri siti SO - se è relativo a VIM, mi piacerebbe trovarlo qui.
Craigp,

Risposte:


21

Ciò accade quando viene invocato vim ed è collegato all'output della pipeline precedente, anziché al terminale e riceve input imprevisti diversi (come NUL). Lo stesso accade quando si esegue:, vim < /dev/nullquindi il resetcomando in questo caso aiuta. Ciò è spiegato bene dalla gravità di superutente .

Se stai usando findper passare i nomi dei file da modificare, non è necessario xargs, basta usare -exec, ad esempio:

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

Se vuoi usare xargs, su Unix / macOS dovresti usare un -oparametro, come:

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

-oRiaprire stdin come / dev / tty nel processo figlio prima di eseguire il comando. Ciò è utile se si desidera che xargs esegua un'applicazione interattiva.

o:

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

Nota: l'utilizzo di -print0/ -0supporterà i nomi di file con spazi bianchi.


find+ BSDxargs

Su Unix / macOS puoi provare la seguente soluzione alternativa:

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

È inoltre possibile utilizzare la sintassi di sostituzione dei comandi , ad esempio:

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

In alternativa, utilizzare GNU parallelinvece di xargsforzare l'allocazione di tty, ad esempio:

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

Nota: parallelsu Unix / OSX non funzionerà in quanto ha parametri diversi e non supporta tty.

Molti altri comandi popolari forniscono anche un'allocazione pseudo-tty (come -tin ssh), quindi cerca aiuto.

Altro suggerimento sarebbe di usare:

Relazionato:


Il mio GNU xargsnon ha -J. Questa sembra essere un'opzione per MacOS (BSD) che non è presente nella versione GNU.
In pausa fino a nuovo avviso.

12

Suggerimento: utilizzare un buffer come navigatore del filesystem

Utilizzare il vim -comando per leggere un elenco di percorsi da stdin. Vim's :help --spiega questo: 1

Inizia a modificare un nuovo buffer, che viene riempito con il testo letto da stdin. I comandi che normalmente verrebbero letti da stdin verranno ora letti da stderr. Esempio:

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

È quindi possibile utilizzare gfo Ctrl-wCtrl-fper passare al file nello stesso buffer o rispettivamente in una nuova finestra divisa.

Suggerimento soluzione alternativa: elenco argomenti

Un altro ottimo modo è usare l'elenco degli argomenti di Vim. Il :argadd **/*.javacomando di popolerà lista degli argomenti di Vim con tutti i file java trovati all'interno della directory corrente in modo ricorsivo. È quindi possibile utilizzare :nexte :prevper spostarsi tra i file.


1 Il primo -dice a Vim che vuoi cercare nella guida un'opzione della riga di comando, il secondo è in realtà il flag della riga di comando che stai cercando. Leggi :help help-contextper altri trucchi come questo.


8

Inoltre reset, puoi provare:

stty sane

che dovrebbe anche rendere nuovamente utilizzabile il tuo terminale.

Vedi qui per le spiegazioni. E in qualche modo questo può essere considerato un comportamento errato vim, almeno Neovim non ha questo problema al momento.


Questo non risponde esattamente alla domanda, ma mi ha aiutato, quindi +1.
Ripristina Monica iamnotmaynard il

Questo risponde alla mia domanda;) Anche se dopo aver letto l'OP di più credo che resetdovrebbe anche.
Sridhar Sarnobat,

1

La ragione è che xargsgli insiemi stdina /dev/null, mentre vimle esigenze stdindi essere /dev/tty.


xargsSoluzione BSD (ad es. Mac):

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

-oimposta il stdinprocesso figlio di xarg ( vimin questo caso) su dev/tty.


xargsSoluzione GNU (es. Linux):

GNU xargsnon ha l' -oopzione. Dovrai invece utilizzare una soluzione più complicata. (Nota: è molto importante avere la zerostringa finale , non dimenticarla.)

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

Puoi anche renderlo un alias:

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

Spiegazione dettagliata della soluzione GNU xargs

Analizziamolo passo dopo passo:

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

1. xargsaggiunge semplicemente stdinla fine della stringa, quindi xargseseguirà questo:

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

2. Il formato per bash -cè bash -c 'COMMAND_STRING' $0 $1 $2 etc.

"$@"si espande nei parametri posizionali "$1", "$2", ecc. Non include "$ 0" perché si tratta di un parametro speciale per il nome dello script, non di un parametro posizionale. Questo è il motivo per cui dobbiamo aggiungere la stringa fittizia zero(può essere qualsiasi stringa) per prendere il posto $0. Altrimenti, perderai il primo file.

Quindi dopo l'espansione "$@", si finisce con:

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

3. bash -ceseguirà COMMAND_STRING:

    </dev/tty vim file1 file2 

</dev/ttyimposta stdinsu in /dev/ttymodo che vimpossa funzionare in modalità interattiva.


+1 Questo sembra essere un duplicato della risposta con il maggior numero di voti, ma non lo è. Il $0segnaposto (qui "zero") è la chiave.
In pausa fino a nuovo avviso.

0

Ho trovato carenti tutte queste risposte. Dopo aver affrontato il problema di xargs, il modo più semplice - per me! - di fare queste ricerche e aprirle su un box generico è il seguente:

vim -O `grep -lir mySearchTerm *`

Non ho problemi ad usare findcon xargse grep, ma trovo fastidiosa la sintassi. E come programmatore quotidiano che usa vime il terminale come loro IDE, e cerca costantemente i file per le proprietà, questo è quello che ho usato.

C'è un tempo e un luogo per find . -path ./node_modules -prune -o -name '*.js' | xargs grep -i mySearchTermaccoppiare l'apertura dei file tramite VIM e altri metodi. Per me, è raro.

Spero che questo aiuti qualcuno.


2
FWIW si consiglia di non utilizzare la notazione di backtick legacy e di utilizzare $(...)invece. Non è così importante nella tua riga di comando come in uno script, ma penso che sia ancora meglio usarlo nelle tue risposte :) (riferimenti qui e qui )
statox
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.