Question Invoquer vi par trouver | xargs casse mon terminal. Pourquoi?


En invoquant vim par find | xargs, comme ça:

find . -name "*.txt" | xargs vim

vous obtenez un avertissement à propos de

Input is not from a terminal

et un terminal avec un comportement à peu près cassé après. Pourquoi donc?


119
2017-09-15 16:26


origine


Note latérale: Vous pouvez effectuer cette opération entièrement dans vim, sans utiliser find ou xargs du tout. Ouvrez vim sans arguments, puis exécutez :args **/*.txt<CR>définir les arguments de vim à partir de l'éditeur. - Trevor Powell
@TrevorPowell: Durant toutes ces années, vim n'a jamais cessé de m'étonner. - DevSolar
En relation: grep -l .. | xargs vim génère un avertissement, pourquoi? à unix SE - kenorb
En relation: Terminal borked après avoir invoqué Vim avec xargs chez Vim SE. - kenorb
Rapport de bug GitHub: vim ne gère pas STDIN défini sur / dev / null. - kenorb


Réponses:


Lorsque vous invoquez un programme via xargs, le stdin du programme (entrée standard) pointe vers /dev/null. (Puisque xargs ne connaît pas le original stdin, il fait la meilleure chose suivante.)

$ true | xargs filan -s
    0 chrdev / dev / null
    1 tty / dev / pts / 1
    2 tty / dev / pts / 1

$ true | xargs ls -l / dev / fd /

Vim s'attend à ce que son stdin soit identique à son terminal de contrôle et effectue diverses ioctlest directement sur stdin. Une fois terminé /dev/null (ou tout descripteur de fichier non-tty), ces ioctls n'ont pas de sens et renvoient ENOTTY, qui est ignoré en silence.

  • Je pense à une cause plus spécifique: au démarrage, Vim lit et mémorise les anciens paramètres du terminal et les restaure à la sortie. Dans notre cas, lorsque les "anciens paramètres" sont demandés pour un descripteur de fichier (fd) autre que tty, Vim reçoit toutes les valeurs vides et toutes les options désactivées, et définit de manière imprudente la même chose pour votre terminal.

    Vous pouvez le voir en courant vim < /dev/null, en sortant, puis en cours d'exécution stty, qui produira beaucoup de <undef>s. Sous Linux, en cours d'exécution stty sane rendra le terminal utilisable à nouveau (même si volonté ont perdu des options telles que iutf8, causant éventuellement des ennuis mineurs plus tard).

Vous pourriez considérer cela comme un bug dans Vim, car pouvez ouvrir /dev/tty pour le contrôle terminal, mais pas. (À un moment donné au cours du démarrage, Vim duplique son stderr en stdin, ce qui lui permet de lire vos commandes d'entrée - à partir d'une fd ouverte pour l'écriture - mais cela ne se fait pas assez tôt.)


81
2017-09-15 16:41



... avec un épanouissement. Merci beaucoup! - DevSolar
+1, et pour TL, les gens DR courent stty sane - rahmanisback
@rahmanisback: Les autres réponses, plus les commentaires de Trevor, ont tous permis d'éviter la rupture du terminal. J'ai accepté la réponse de grawity, car ma question était "pourquoi", pas "comment éviter" - qui est couvert par une autre question que réellement engendré celui-là. - DevSolar
@DevSolar Compris, mais pensez aux gens frustrés comme moi qui ne font que google pour se débarrasser de ce comportement alors que ce n'est pas - malheureusement - assez de temps pour étudier "pourquoi", ce qui est néanmoins très intéressant. - rahmanisback
quand mon terminal se casse, comme ça, j'utilise reset au lieu de stty sane et ça marche bien après ça. - Capi Etheriel


Suite à la réponse de grawity, xargs points stdin à /dev/null


De OSX / BSD man xargs

-o Reopen stdin comme / dev / tty dans le processus fils
        avant d'exécuter la commande. C'est utile
        Si vous voulez que xargs exécute une application interactive.

Ainsi, la ligne de code suivante devrait fonctionner pour vous:

trouver . -name "* .txt" | xargs -o vim

Pour GNU man xargs il n'y a pas de drapeau, mais nous pouvons explicitement passer / dev / tty pour résoudre le problème:

trouver . -name "* .txt" | xargs bash -c '</ dev / tty vim "$ @"' ignoreme

l'ignoreme est la pour prendre $ 0, de sorte que $ @ est tous les arguments de xargs


117
2018-05-23 12:15



Comment pourriez-vous créer un alias bash à partir de cela? $@ ne semble pas traduire correctement les arguments. - zanegray
@zanegray - vous ne pouvez pas créer un alias, mais vous pouvez en faire une fonction. Essayer: function vimin () { xargs sh -c 'vim "$@" < /dev/tty' vim; } - Christopher


Le moyen le plus simple:

vim $(find . -name "*foo*")

28
2018-03-08 02:13



La question principale était "pourquoi", et non "comment l'éviter", et il a été répondu à la satisfaction il y a deux ans et demi. - DevSolar
Cela, bien sûr, ne fonctionne pas correctement lorsque les noms de fichiers contiennent des espaces ou d'autres caractères spéciaux, et constitue également un risque de sécurité. - Dejay Clayton
Ma réponse préférée, car elle fonctionne pour toutes les commandes qui répertorient des fichiers, pas seulement "find" ou des caractères génériques. Cela nécessite un peu de confiance, comme le souligne Dejay. - Travis Wilson
Cela ne fonctionnera pas avec de nombreux cas d'utilisation pour lesquels xargs est conçu: par exemple, lorsque le nombre de chemins est très élevé (cc @ TravisWilson) - Good Person


Cela devrait fonctionner correctement si vous utilisez l'option -exec sur find plutôt que de passer dans xargs. Par exemple

$ find . -type f -name filename.txt -exec vi {} +


19
2018-03-10 14:31



Huh ... l'astuce est la + (au lieu de "l'habituel" \;) pour obtenir tous les fichiers trouvés dans un Session Vim - une option I garder oublier. Vous avez raison, bien sûr, et +1 pour cela. j'utilise vim $(find ...) simplement par habitude. Cependant, je demandais en fait Pourquoi l'opération de tuyauterie visse le terminal, et le grawity l'a cloué avec son explication. - DevSolar
C'est la meilleure réponse et cela fonctionne à la fois sur BSD / OSX / GNU / Linux. - kevinarpe
De plus, find n'est pas le seul moyen d'obtenir une liste de fichiers à éditer simultanément par vim. Je peux utiliser grep pour trouver tous les fichiers avec un motif et essayer de les éditer en même temps. - Chandranshu


Utilisez plutôt GNU Parallel:

find . -name "*.txt" | parallel -j1 --tty vim

Ou si vous souhaitez ouvrir tous les fichiers en une seule fois:

find . -name "*.txt" | parallel -Xj1 --tty vim

Il traite même correctement les noms de fichiers comme:

My brother's 12" records.txt

Regardez la vidéo d'introduction pour en savoir plus: http://www.youtube.com/watch?v=OpaiGYxkSuQ


8
2017-09-16 17:45



Pas disponible partout. La plupart du temps, je travaille sur des serveurs sur lesquels je ne peux pas installer d’outils supplémentaires. Mais merci pour le conseil quand même. - DevSolar
Si vous êtes libre de faire "cat> file"; chmod + x file 'alors vous pouvez installer GNU Parallel: c'est simplement un script perl. Si vous voulez des pages de manuel et autres, vous pouvez l'installer sous votre répertoire d'hôte: ./configure --prefix = $ HOME && make && make install - Ole Tange
OK, essayé cela - mais parallèlement fait ne pas ouvrir tous les fichiers, il les ouvre successivement. C'est aussi une bouchée pour une opération simple. vim $(find . -name "*.txt") est plus simple, et vous obtenez tous les fichiers ouverts en même temps. - DevSolar
@DevSolar: Quelque peu sans rapport, mais les deux find | xargs et $(find) aura de gros problèmes avec des espaces dans les noms de fichiers. - grawity
@grawity Correct, mais il n'y a pas de solution facile (que je connais). Tu devrais commencer à jouer avec $IFS, -print0 Et puis, vous avez quitté le domaine de la solution en ligne de commande et avez atteint un point où vous devriez trouver un script ... il y a une raison pour laquelle les espaces dans les noms de fichiers sont déconseillés. - DevSolar