Remarque secondaire: vous pouvez effectuer cette opération entièrement dans vim, sans utiliser findou pas xargsdu tout. Ouvrez vim sans argument, puis exécutez-le :args **/*.txt<CR>pour définir les arguments de vim à partir de l'éditeur.
Trevor Powell
3
@TrevorPowell: Au cours de toutes ces années, Vim n'a jamais cessé de m'étonner.
Lorsque vous appelez un programme via xargs, le stdin (entrée standard) du programme pointe sur /dev/null. (Puisque xargs ne connait pas le stdin original , c'est la meilleure chose à faire.)
$ 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 à celui de son terminal de contrôle et exécute directement divers ioctls liés au terminal sur stdin. Lorsque cela est fait sur /dev/null(ou tout descripteur de fichier non-tty), ces ioctls n'ont pas de sens et renvoient ENOTTY, ce qui est ignoré en silence.
À mon avis, une cause plus spécifique: Au démarrage, Vim lit et retient les anciens paramètres du terminal, puis les restaure à la sortie. Dans notre cas, lorsque les "anciens paramètres" sont demandés pour un descripteur de fichier non-tty, Vim reçoit toutes les valeurs vides et toutes les options désactivées, et définit négligemment la même chose sur votre terminal.
Vous pouvez voir cela en exécutant vim < /dev/null, en le quittant, puis en exécutant stty, ce qui produira beaucoup de <undef>s. Sous Linux, l'exécution stty sanerendra le terminal utilisable à nouveau (bien qu'il aura perdu des options telles que iutf8, causant éventuellement des ennuis mineurs plus tard).
Vous pourriez considérer cela comme un bogue dans Vim, car il peut s'ouvrir /dev/ttypour le contrôle de terminal, mais ne le fait pas. (À un moment donné au cours du démarrage, Vim duplique son stderr sur stdin, ce qui lui permet de lire vos commandes d'entrée - depuis un fd ouvert en écriture - mais même cela n'est pas fait assez tôt.)
+1, et pour TL; DR, les gens viennent de courirstty sane
doc_id
@rahmanisback: Les autres réponses, ainsi que le commentaire de Trevor, fournissaient tous des moyens d'éviter la casse finale. J'ai accepté la réponse de grawity, parce que ma question était "pourquoi", pas "comment éviter" - c'est couvert par une autre question qui a en fait engendré celle-ci.
DevSolar
@DevSolar Compris, mais pensez aux personnes frustrées comme moi qui cherchent simplement à se débarrasser de ce comportement tout en ayant - malheureusement - assez de temps pour étudier le "pourquoi", ce qui est néanmoins très intéressant.
doc_id
4
quand mon terminal se casse, comme ça, je l’utilise à la resetplace stty saneet ça marche très bien après ça.
Capi Etheriel
137
(Suite à l'explication de grawity, cela xargspointe stdinvers /dev/null.)
La solution à ce problème consiste à ajouter le -oparamètre à xargs. De man xargs:
-o
Rouvrez stdin comme /dev/ttydans le processus enfant avant d'exécuter la commande. Ceci est utile si vous souhaitez xargsexécuter une application interactive.
Ainsi, la ligne de code suivante devrait fonctionner pour vous:
trouver . -name "* .txt" | xargs -o vim
GNU xargs supporte cette extension depuis certaines versions en 2017 (avec le nom de l'option longue --open-tty).
Pour les versions antérieures ou autres de xargs, vous pouvez explicitement vous connecter /dev/ttypour résoudre le problème:
Comment créerais-tu un alias bash? $@ne semble pas traduire correctement les arguments.
zanegray
1
@zanegray - vous ne pouvez pas créer d'alias, mais vous pouvez en faire une fonction. Essayez:function vimin () { xargs sh -c 'vim "$@" < /dev/tty' vim; }
Christopher
Pour une explication détaillée du fonctionnement de la solution GNU xargs et de la raison pour laquelle vous avez besoin de la ignoremechaîne factice , voir vi.stackexchange.com/a/17813
wisbucky
@zanegray, vous pouvez en faire un alias. Les citations sont délicates. Voir la solution sur vi.stackexchange.com/a/17813
wisbucky
The -J, -o, -P and -R options are non-standard FreeBSD extensions which may not be available on other operating systems.(Il n'était pas disponible sur macOS pour moi parce que j'ai installé xargs depuis homebrew (celui de GNU))
La question principale était "pourquoi", pas "comment l'éviter", et on y a répondu avec satisfaction il y a deux ans et demi.
DevSolar
5
Ceci, 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 pour la sécurité.
Dejay Clayton
1
Ma réponse préférée parce que cela fonctionne pour toutes les commandes qui répertorient des fichiers, pas seulement "trouver" ou des caractères génériques. Cela demande un peu de confiance, comme le souligne Dejay.
Travis Wilson
1
Cela ne fonctionnera pas avec de nombreux cas d'utilisation xargs est conçu pour: par exemple, lorsque le nombre de chemins est très élevé (cc @TravisWilson)
Bonne personne
21
Cela devrait fonctionner correctement si vous utilisez l'option -exec sur find plutôt que de vous connecter à xargs.
Euh ... l'astuce consiste à obtenir +(au lieu de "l'habituel" \;) tous les fichiers trouvés en une session Vim - une option que je ne cesse d'oublier. Vous avez raison, bien sûr, et +1 pour cela. J'utilise vim $(find ...)simplement par habitude. Cependant, je demandais en fait pourquoi le système de tuyauterie visait le terminal, et Grawity l'a expliqué dans ses explications.
DevSolar
2
C'est la meilleure réponse et cela fonctionne à la fois sous BSD / OSX / GNU / Linux.
kevinarpe
1
En outre, find n'est pas le seul moyen d'obtenir une liste de fichiers devant être modifiés 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
8
Utilisez GNU Parallel à la place:
find . -name "*.txt" | parallel -j1 --tty vim
Ou si vous voulez ouvrir tous les fichiers en une fois:
find . -name "*.txt" | parallel -Xj1 --tty vim
Il traite même correctement les noms de fichiers tels que:
Pas disponible partout. La majeure partie de la journée, je travaille sur des serveurs où je ne suis pas libre d’installer des outils supplémentaires. Mais merci pour l'allusion quand même.
DevSolar
Si vous êtes libre de faire 'cat> file; chmod + x fichier ', vous pouvez installer GNU Parallel: c’est simplement un script Perl. Si vous voulez les pages de manuel et tel, vous pouvez l' installer sous votre homedir: ./configure prefix = $ HOME && make && make install
Ole Tange
2
OK, j'ai essayé - mais parallèlement, tous les fichiers ne sont pas ouverts, 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 à la fois.
DevSolar
5
@ DevSolar: Un peu sans rapport, mais les deux find | xargset $(find)auront de gros problèmes d'espaces dans les noms de fichiers.
Grawity
2
@grawity Correct, mais il n'y a pas de moyen facile de le contourner (à ma connaissance). Vous devez commencer à bricoler $IFS, -print0et cetera, puis vous avez quitté le domaine des solutions de ligne de commande one-shot pour atteindre un point où vous devriez créer un script ... il y a une raison pour laquelle les espaces dans les noms de fichiers sont découragés .
DevSolar
0
peut-être pas le meilleur mais voici le script que j'utilise (nommé vim-open):
Pour ce qui est de plusieurs autres réponses, notez que la vraie question était "pourquoi" et non "comment l'éviter". (Pour lequel je signalerais toujours le commentaire de Trevor sous ma question comme le moyen le plus solide qui n'exige pas de script, d'alias ou quoi que ce soit.)
find
ou pasxargs
du tout. Ouvrez vim sans argument, puis exécutez-le:args **/*.txt<CR>
pour définir les arguments de vim à partir de l'éditeur.grep -l .. | xargs vim
génère un avertissement, pourquoi? à unix SERéponses:
Lorsque vous appelez un programme via
xargs
, le stdin (entrée standard) du programme pointe sur/dev/null
. (Puisque xargs ne connait pas le stdin original , c'est la meilleure chose à faire.)Vim s'attend à ce que son stdin soit identique à celui de son terminal de contrôle et exécute directement divers ioctls liés au terminal sur stdin. Lorsque cela est fait sur
/dev/null
(ou tout descripteur de fichier non-tty), ces ioctls n'ont pas de sens et renvoient ENOTTY, ce qui est ignoré en silence.À mon avis, une cause plus spécifique: Au démarrage, Vim lit et retient les anciens paramètres du terminal, puis les restaure à la sortie. Dans notre cas, lorsque les "anciens paramètres" sont demandés pour un descripteur de fichier non-tty, Vim reçoit toutes les valeurs vides et toutes les options désactivées, et définit négligemment la même chose sur votre terminal.
Vous pouvez voir cela en exécutant
vim < /dev/null
, en le quittant, puis en exécutantstty
, ce qui produira beaucoup de<undef>
s. Sous Linux, l'exécutionstty sane
rendra le terminal utilisable à nouveau (bien qu'il aura perdu des options telles queiutf8
, causant éventuellement des ennuis mineurs plus tard).Vous pourriez considérer cela comme un bogue dans Vim, car il peut s'ouvrir
/dev/tty
pour le contrôle de terminal, mais ne le fait pas. (À un moment donné au cours du démarrage, Vim duplique son stderr sur stdin, ce qui lui permet de lire vos commandes d'entrée - depuis un fd ouvert en écriture - mais même cela n'est pas fait assez tôt.)la source
stty sane
reset
placestty sane
et ça marche très bien après ça.(Suite à l'explication de grawity, cela
xargs
pointestdin
vers/dev/null
.)La solution à ce problème consiste à ajouter le
-o
paramètre àxargs
. Deman xargs
:Ainsi, la ligne de code suivante devrait fonctionner pour vous:
GNU xargs supporte cette extension depuis certaines versions en 2017 (avec le nom de l'option longue
--open-tty
).Pour les versions antérieures ou autres de xargs, vous pouvez explicitement vous connecter
/dev/tty
pour résoudre le problème:(Le
ignoreme
est là pour prendre $ 0, donc $ @ est tous les arguments de xargs.)la source
$@
ne semble pas traduire correctement les arguments.function vimin () { xargs sh -c 'vim "$@" < /dev/tty' vim; }
ignoreme
chaîne factice , voir vi.stackexchange.com/a/17813The -J, -o, -P and -R options are non-standard FreeBSD extensions which may not be available on other operating systems.
(Il n'était pas disponible sur macOS pour moi parce que j'ai installé xargs depuis homebrew (celui de GNU))Le moyen le plus simple:
la source
Cela devrait fonctionner correctement si vous utilisez l'option -exec sur find plutôt que de vous connecter à xargs.
la source
+
(au lieu de "l'habituel"\;
) tous les fichiers trouvés en une session Vim - une option que je ne cesse d'oublier. Vous avez raison, bien sûr, et +1 pour cela. J'utilisevim $(find ...)
simplement par habitude. Cependant, je demandais en fait pourquoi le système de tuyauterie visait le terminal, et Grawity l'a expliqué dans ses explications.Utilisez GNU Parallel à la place:
Ou si vous voulez ouvrir tous les fichiers en une fois:
Il traite même correctement les noms de fichiers tels que:
Regardez la vidéo d'introduction pour en savoir plus: http://www.youtube.com/watch?v=OpaiGYxkSuQ
la source
vim $(find . -name "*.txt")
est plus simple, et vous obtenez tous les fichiers ouverts à la fois.find | xargs
et$(find)
auront de gros problèmes d'espaces dans les noms de fichiers.$IFS
,-print0
et cetera, puis vous avez quitté le domaine des solutions de ligne de commande one-shot pour atteindre un point où vous devriez créer un script ... il y a une raison pour laquelle les espaces dans les noms de fichiers sont découragés .peut-être pas le meilleur mais voici le script que j'utilise (nommé
vim-open
):travaillera avec
vim-open a b c
etls | vim-open
par exemplela source