Terminal borked après avoir appelé Vim avec xargs

30

J'ai parfois essayé d'invoquer Vim en utilisant xargs, comme ceci:

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

… Quel genre d'œuvres:

  1. Au lancement de Vim, je vois brièvement le flash d'avertissement suivant:

    Vim: Warning: Input is not from a terminal
    
  2. L'édition fonctionne - :filesénumère correctement tous les .javafichiers comme prévu.
  3. Je peux enregistrer et quitter.

Cependant, après avoir quitté Vim, mon terminal est bouché:

  • Tout ce que je tape à l'invite du shell n'est pas répété.
  • Les retours chariot n'apparaissent pas du tout et les sauts de ligne n'apparaissent que parfois.

Cela continue jusqu'à ce que j'émette une reset(1)commande pour réinitialiser le terminal.

Est-ce un bug Vim, ou y a-t-il une explication plus satisfaisante pour expliquer pourquoi il interagit avec le terminal comme ça? Je l'ai vu se produire sur Vim jusqu'à la version 7.3 (la version ne semble pas avoir d'importance) sur Linux et divers Unices.

Je connais une solution de contournement, à savoir vim $(find . -name '*.java'). D'autres solutions de contournement seraient les bienvenues, mais ce n'est pas ma principale question.

200_success
la source
Je reçois en quelque sorte votre désir de remplir ce site avec autant de questions que possible mais… cette question a été répondue des dizaines de fois au fil des ans sur SO / SU et ailleurs: xargsutilise un mannequin stdinqui ne peut pas être utilisé par Vim et fait une pause tout après.
romainl
1
@romainl Je ne suis pas actif sur ces sites, et je ne l'ai jamais vu poser la question - honnêtement.
200_success
Connexes: superuser.com/questions/336016/… - peut-être que quelqu'un peut condenser les meilleures réponses pour une excellente réponse.
muru
2
Grande question - cela m'est arrivé plusieurs fois. De plus, je n'utilise aucun autre site SO - s'il est lié à vim, j'aimerais le trouver ici.
craigp

Réponses:

21

Cela se produit lorsque vim est appelé et qu'il est connecté à la sortie du pipeline précédent, au lieu du terminal et qu'il reçoit différentes entrées inattendues (comme des NUL). La même chose se produit lorsque vous exécutez:, vim < /dev/nulldonc la resetcommande dans ce cas est utile. Cela s'explique bien par la gravité du superutilisateur .

Si vous utilisez findpour passer des noms de fichiers à modifier, vous n'avez pas besoin xargs, utilisez simplement -exec, par exemple:

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

Si vous souhaitez utiliser xargs, sur Unix / macOS, vous devez utiliser un -oparamètre, comme:

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

-oRouvrez stdin en tant que / dev / tty dans le processus enfant avant d'exécuter la commande. Ceci est utile si vous souhaitez que xargs exécute une application interactive.

ou:

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

Remarque: L'utilisation de -print0/ -0prendra en charge les noms de fichiers avec des espaces.


find+ BSDxargs

Sous Unix / macOS, vous pouvez essayer la solution de contournement suivante:

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

Vous pouvez également utiliser la syntaxe de substitution de commandes , par exemple:

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

Vous pouvez également utiliser GNU parallelau lieu de xargspour forcer l'allocation tty, par exemple:

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

Remarque: parallelsous Unix / OSX ne fonctionnera pas car il a des paramètres différents et ne prend pas en charge tty.

De nombreuses autres commandes populaires fournissent également une allocation pseudo-tty (comme -tdans ssh), alors vérifiez l'aide.

Une autre suggestion serait d'utiliser:

En relation:

kenorb
la source
Mon GNU xargsn'en a pas -J. Cela semble être une option MacOS (BSD) qui n'est pas présente dans la version GNU.
pause jusqu'à nouvel ordre.
12

Solution de contournement: utilisez un tampon comme navigateur de système de fichiers

Utilisez la vim -commande pour lire une liste de chemins depuis stdin. Vim :help --explique ceci: 1

Commencez à éditer un nouveau tampon, qui est rempli de texte lu depuis stdin. Les commandes qui seraient normalement lues depuis stdin seront désormais lues depuis stderr. Exemple:

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

Vous pouvez ensuite utiliser gfou Ctrl-wCtrl-fpour naviguer vers le fichier dans le même tampon ou dans une nouvelle fenêtre divisée respectivement.

Solution de contournement: liste d'arguments

Un autre excellent moyen est d'utiliser la liste d'arguments de Vim. La :argadd **/*.javacommande remplira la liste d'arguments de Vim avec tous les fichiers java trouvés dans le répertoire courant de manière récursive. Vous pouvez ensuite utiliser les touches :nextet :prevpour vous déplacer entre les fichiers.


1 Le premier -indique à Vim que vous souhaitez rechercher dans l'aide une option de ligne de commande, le second est en fait l'indicateur de ligne de commande que vous recherchez. Lisez :help help-contextpour plus d'astuces comme celle-ci.

akshay
la source
8

De plus reset, vous pouvez essayer:

stty sane

ce qui devrait également rendre votre terminal réutilisable.

Voir ici pour des explications. Et d'une manière ou d'une autre, cela peut être considéré comme une mauvaise conduite de Vim , au moins Neovim n'a pas ce problème pour le moment.

ryenus
la source
Cela ne répond pas exactement à la question, mais cela m'a aidé, alors +1.
Rétablir Monica iamnotmaynard
Cela répond à ma question;) Bien qu'après avoir lu l'OP, je suppose que je resetdevrais aussi.
Sridhar Sarnobat
1

La raison est que les xargsensembles stdinà /dev/null, alors que les vimbesoins stdind'être /dev/tty.


xargsSolution BSD (par exemple Mac):

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

-odéfinit le stdinprocessus enfant de xarg ( vimdans ce cas) sur dev/tty.


xargsSolution GNU (par exemple Linux):

GNU xargsn'a pas l' -ooption. Au lieu de cela, vous devrez utiliser une solution de contournement plus compliquée. (Remarque: il est très important d'avoir la zerochaîne de fin , ne l'oubliez pas.)

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

Vous pouvez également en faire un alias:

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

Explication détaillée de la solution GNU xargs

Décomposons-le étape par étape:

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

1. xargsajoute simplement le stdinà la fin de la chaîne, donc xargsexécutera ceci:

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

2. Le format de bash -cest bash -c 'COMMAND_STRING' $0 $1 $2 etc.

"$@"s'étend aux paramètres positionnels "$1", "$2", etc. Il n'inclut pas "$ 0" car il s'agit d'un paramètre spécial pour le nom du script, pas d'un paramètre positionnel. C'est pourquoi nous devons ajouter la chaîne factice zero(il peut s'agir de n'importe quelle chaîne) pour prendre la place $0. Sinon, vous perdrez le premier fichier.

Donc, après avoir développé "$@", vous vous retrouvez avec:

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

3. bash -cexécutera la COMMAND_STRING:

    </dev/tty vim file1 file2 

</dev/ttydéfinit la stdinà /dev/ttysorte que vimpeut travailler en mode interactif.

wisbucky
la source
+1 Cela semble être un doublon de la réponse avec le plus grand nombre de votes, mais ce n'est pas le cas. L' $0espace réservé (ici "zéro") est la clé.
pause jusqu'à nouvel ordre.
0

J'ai trouvé que toutes ces réponses manquaient. Après avoir traversé le problème des xargs, la façon la plus simple - pour moi! - de faire ces recherches et ouvertures sur une boîte générique est la suivante:

vim -O `grep -lir mySearchTerm *`

Je n'ai aucun problème à utiliser findavec xargset grep, mais je trouve la syntaxe ennuyeuse. Et en tant que codeur quotidien qui utilise vimle terminal comme IDE et recherche constamment des propriétés dans les fichiers, c'est ce que j'utilise.

Il y a un temps et un lieu pour find . -path ./node_modules -prune -o -name '*.js' | xargs grep -i mySearchTermcoupler l'ouverture de fichiers via vim et d'autres méthodes. Pour moi, c'est rarement.

J'espère que cela aide quelqu'un.

Gavin
la source
2
FWIW, il est recommandé de ne pas utiliser la notation héritée des raccourcis et de l'utiliser à la $(...)place. Ce n'est pas aussi important dans votre ligne de commande que dans un script mais je pense qu'il vaut toujours mieux l'utiliser dans vos réponses :) (références ici et ici )
statox