Affichage de la sortie standard d'un processus d'arrière-plan à un emplacement spécifique du terminal

8

J'ai une commande que j'exécute chaque fois qu'un nouveau terminal est ouvert ou qu'une nouvelle connexion est établie.

Ce programme produit une sortie (colorée) qui doit être positionnée avant l'invite de commande. L'exécution peut prendre quelques secondes, ce qui m'empêchera d'utiliser le terminal jusque-là (sauf s'il est exécuté en arrière-plan).

Étant donné que zsh a des moyens avancés de redessiner le terminal sans encombrer le texte existant, je voudrais savoir comment puis-je exécuter cette commande d'une manière que je n'ai pas à attendre qu'elle se termine avant de pouvoir utiliser le terminal, mais que une fois terminé, il imprime la sortie comme si elle n'était pas en arrière-plan en premier lieu.

En pratique, j'aimerais quelque chose qui pourrait faire:

Command output:
... (running on background)
me@computer: somecommand
me@computer: someothercommand

et une fois la commande terminée, j'obtiendrais:

Command output:
 * Output foo
 * Multiple lines bar
 * and some yada
me@computer: somecommand
me@computer: someothercommand

J'ai essayé de mettre le processus en arrière-plan au début, mais il n'affiche pas correctement la sortie. Je reçois quelque chose comme:

Command output:
[2] 32207
me@computer: somecommand
me@computer: someother * Output foo
 * Multiple lines bar
 * and some yada
[2]  + done       command
me@computer: someothercommand

Alors, est-ce possible? Si ce n'est pas avec zsh, y a-t-il une solution qui pourrait le faire?

Tous les pointeurs ou informations sont les bienvenus.

unode
la source
Ce n'est pas possible si le programme s'exécute directement dans le terminal, car il n'y a qu'une seule position de curseur et zsh et le programme serait en concurrence pour cela. Vous pouvez faire quelque chose avec zpty: faire exécuter la commande dans un terminal créé par zsh, avec sa sortie relayée au terminal réel sous le contrôle de zsh.
Gilles 'SO- arrête d'être méchant'
@Gilles Pourriez-vous nous en dire plus sur l'utilisation de zpty? Je suis conscient que vous ne pouvez pas avoir deux programmes écrivant sur le même terminal sans une sorte de concurrence sur le curseur. Ma question vient du fait que zsh réécrit déjà l'invite dans certaines configurations. De toute évidence, ce programme devrait écrire dans un tampon temporaire en mémoire qui serait ensuite "imprimé" à l'emplacement spécifique une fois la commande terminée. Tous ces derniers sous le contrôle de zsh.
onode le
@Gilles: peut-être que cela vaut une vraie réponse?
Stéphane Gimenez
@ StéphaneGimenez Bien sûr que ça l'est. Je ne sais pas comment le faire sans quelques recherches, et je n'ai pas le temps pour ces recherches en ce moment.
Gilles 'SO- arrête d'être méchant'
Une idée serait de le faire screenavec un script de démarrage qui divise l'écran et exécute le long runner dans la partie supérieure et la ligne de commande normale dans la partie inférieure.
vasquez

Réponses:

5

Il s'agit d'une solution simple si vous êtes prêt à accepter une sortie juste au-dessus de la ligne d'invite actuelle.

TRAPUSR1 () { zle -I; unfunction TRAPUSR1 }  # invalidate prompt on signal USR1

bufferout () {
    local buffer
    while read -r line; do                   # buffer lines from stdin
        buffer="$buffer$line\n"
    done
    print -rn -- $terminfo[dl1]              # delete current line
    print -rn -- $terminfo[cr]               # move cursor to BOL
    printf "$buffer"                         # print buffer
    kill -USR1 $$                            # send USR1 when we're done
}

unsetopt monitor                             # don't monitor this job
./testout |& bufferout & disown              # bg and disown to suppress notification
setopt monitor                               # restore job monitoring

Lorsque le travail est terminé, l'invite actuelle et le tampon d'entrée seront supprimés et l'intégralité de la commande stdoutet stderrseront imprimés.

Vous pouvez obtenir beaucoup plus de fantaisie que cela avec le zsh/cursesmodule, mais je doute qu'il offrirait un avantage suffisamment important pour mériter l'effort.

utilisateur112553
la source
Cela marche. Mais à quel point serait-il difficile d'empêcher le shell d'imprimer le pid du processus lorsqu'il est envoyé en arrière-plan et le message "terminé" une fois terminé? (Je veux dire désactiver temporairement cette fonctionnalité). De plus, avant de placer la sortie sur le terminal, à quel point serait-il difficile d'effacer la ou les lignes d'invite existantes?
unode le
Je pense que je pourrais avoir une autre configuration de réécriture d'invite qui entre en collision avec la suppression de l'invite initiale. Le mien ne disparaît pas une fois la commande terminée. J'obtiens également toujours les PID des processus lorsqu'ils sont envoyés en arrière-plan, mais la ligne "terminé" a disparu. En tout cas, j'accepte votre réponse car elle est assez proche de ce que j'avais en tête. Merci.
unode le
J'utilise xterm et j'ai le module VCS activé et quelques autres personnalisations à l'invite. Je n'ai pas essayé mais je suis sûr que c'est lié à ça. Il y a quelque temps, j'ai essayé d'utiliser le notificateur de mode vi similaire à celui-ci ( stackoverflow.com/a/3791786/125801 ) et lorsqu'il était actif, j'ai perdu une partie de mon comportement d'invite personnalisée. Ce n'est pas grave, pour le moment je suis satisfait de la solution. Je vais essayer de nettoyer mon invite un de ces jours et vérifier si les PID apparaissent toujours.
onode le
Zsh-4.3.10, après avoir désactivé à peu près toutes les personnalisations, je ne reçois plus l'invite (c'est-à-dire qu'il est correctement supprimé) mais j'obtiens toujours les PID des processus en arrière-plan, comme [1] 27911 27912sur une seule ligne et comme première ligne sur le Terminal.
onode
Puis-je voter à nouveau? :) C'est parfait maintenant! Merci beaucoup.
onode