Zsh peut-il accéder à la sortie standard du dernier programme exécuté?


15

J'utilise souvent findou locatepour découvrir des chemins.

(~) locate foobar.mmpz
/home/progo/lmms/projects/foobar.mmpz

L'étape suivante consiste souvent à ouvrir ou à manipuler les fichiers. Dans un cas heureux comme ci-dessus, je peux le faire:

(~) ls `!!`
ls `locate foobar.mmpz`
/home/progo/lmms/projects/foobar.mmpz

Mais personne n'est trop content quand il y a beaucoup de lignes de sortie, dont certaines peuvent ne pas être des chemins ou autre chose de ce genre. De plus, la réexécution de commandes potentiellement inutiles n'est pas très élégante non plus.

Y aurait-il un moyen de connecter zsh pour stocker la sortie standard dans un tableau pour une manipulation ultérieure? Après tout, c'est le travail du shell de rediriger les flux vers l'utilisateur. Je pense qu'il pourrait stocker les N et N dernières lignes dans une variable pour une utilisation ultérieure immédiate, comme $?et d'autres.

Ok donc c'est plutôt cool: /unix//a/59704/5674 . Je demande maintenant le savoir-faire de zsh (et le portage du code vers zsh) pour truquer ce type de capture après chaque ligne d'exécution.


Ce n'est pas vraiment le travail du shell de rediriger le flux vers l'utilisateur. Les applications écrivent leur sortie sur le terminal, le shell n'y est pas impliqué.
Stéphane Chazelas

@StephaneChazelas, mais c'est le travail du shell de rediriger, par exemple, les flux dans des fichiers selon les demandes de l'utilisateur. Il existe également des recettes pour zsh qui colorent stderr en rouge pour le séparer de stdout. Je pense que cela indique le rôle du shell dans les questions de streaming.
unperson325680

Oui, vous pouvez le faire en utilisant les crochets screenor scriptet precmd et preexec.
Stéphane Chazelas

Réponses:


7

Il n'y a aucune fonction pour capturer la sortie de l'écran sur la plupart des émulateurs de terminal. Il me semble que l'auteur de xterm (l'émulateur de terminal «de référence») a déclaré qu'il serait difficile à mettre en œuvre. Même si cela était possible, le shell devrait garder une trace de la dernière invite.

Ainsi, vous n'échapperez pas à la réexécution de la commande, sauf si vous utilisez un mécanisme manuel spécifique au terminal tel que le copier-coller avec la souris dans xterm ou avec le clavier dans Screen.

Il serait très peu pratique pour le shell de capturer automatiquement la sortie des commandes, car il ne peut pas distinguer les commandes qui ont des interactions complexes entre le terminal et l'utilisateur des commandes qui produisent simplement des caractères imprimables.

Vous pouvez réexécuter la commande et capturer sa sortie. Il existe différentes façons de faire chacun. Pour réexécuter la commande, vous pouvez utiliser:

  • !! substitution d'historique - plus pratique à taper;
  • fc -e -, qui peut être utilisé dans une fonction.

Pour capturer la sortie, vous pouvez utiliser la substitution de commandes ou une fonction comme la suivante:

K () {
  lines=("${(f@)$(cat)}")
}
!! |K

Cela définit le linestableau sur la sortie de la commande qui y est acheminée.


Ainsi soit-il. Je me rends compte maintenant à la lumière de bonnes explications qu'une approche complètement automatique est trop difficile telle quelle, la deuxième meilleure chose est quelque chose comme la Kvôtre.
unperson325680

3

Voici une première coupe de quelque chose pour mettre la dernière ligne de sortie dans une variable appelée $lastline.

precmd () {                                                                                                                                                                                                        
    exec 2>&- >&-
    lastline=$(tail -1 ~/.command.out) 
    sleep 0.1   # TODO: synchronize better
    exec > /dev/tty 2>&1
}

preexec() {
    exec > >(tee ~/.command.out&)
}

Cela utilise le preexeccrochet de zsh pour exécuter execavec teepour stocker une copie de stdout de la commande, puis utilise precmdpour lire la sortie stockée et restaurer stdout pour être juste le terminal pour afficher l'invite.

Mais il faut encore du travail. Par exemple, comme stdout n'est plus un terminal, les programmes tels que vimet lessne fonctionnent pas correctement.

Il y a des informations connexes utiles dans ces questions:


Oui, peut-être qu'une approche 100% automatique ne fonctionnera pas. Il y a beaucoup d' execappels dans le code, la commande est-elle exécutée plusieurs fois ou suis-je pris dans une sémantique particulière?
unperson325680

execsans nom de programme définit simplement les redirections.
Mikel

2

Vous pouvez le faire en canalisant simplement vos résultats vers tee, ce qui enregistre simplement la sortie dans un fichier et l'affiche en même temps.

Ainsi, par exemple, vous pouvez le faire, qui affiche votre sortie comme d'habitude, mais l'enregistre également dans le fichier /tmp/it

locate foobar.mmpz | tee /tmp/it

puis cat ce fichier et grep pour sélectionner des choses, par exemple

cat /tmp/it | grep progo | grep lms

puis pour l'utiliser, vous pouvez simplement faire ceci:

vi $(!!)


2

Je suis venu avec cette solution à moitié cuite:

alias -g ___='"$(eval "$(fc -ln -1)" | tail -n 1)"'

Cela vous permet d'écrire ___à tout moment dans la ligne de commande. La commande précédente sera réexécutée et ___sera remplacée par la dernière ligne de sa sortie. Exemple d'utilisation:

$ touch foo bar baz
$ ls -1
bar
baz
foo
$ vim ___

La dernière commande sera étendue à vim foo.

Cela a des arêtes vives!

  • Si vous incluez ___dans une commande mais que la commande précédente incluait également a ___, le shell se bloque dans un état étrange pendant un certain temps. Vous pouvez sortir de cet état immédiatement avec Ctrl- C.

  • Vous ne pouvez pas non plus appuyer sur Tabpour développer le ___, comme vous le pouvez avec !$et d'autres constructions.

  • Certaines commandes affichent des sorties différentes lorsqu'elles sont exécutées «normalement» et lorsqu'elles sont attachées à un tuyau. (Comparez la sortie de lset ls | cat.) Si la commande déclenchée par ___est l'une d'entre elles, vous pourriez finir par exécuter une commande différente de celle que vous attendiez.

  • Et bien sûr, si vous vouliez faire quelque chose avec une ligne de sortie autre que la dernière, cela ne vous aidera pas.

J'ai choisi le nom ___parce que c'est quelque chose que je n'ai jamais voulu inclure comme mot dans une ligne de commande, pas même comme argument. Vous pouvez choisir un nom différent, mais veillez à ne pas choisir quelque chose qui pourrait être développé pour vous par inadvertance.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.