Comment éliminer les désagréments lors du démarrage d'une interface graphique à partir d'un terminal?

14

Je préfère lancer des applications GUI à partir d'une fenêtre de terminal plutôt qu'en utilisant un bureau graphique. Une gêne fréquente est que souvent les développeurs n'ont pas anticipé ce type d'utilisation, donc l'application imprime beaucoup de messages inutiles, cryptiques ou non informatifs sur stdout ou stderr. Un encombrement supplémentaire sur le terminal se produit car l'exécution du programme en arrière-plan, avec un &, génère des rapports sur la création et la fin du travail.

Quelle est une solution de contournement pour ces problèmes qui acceptera les arguments de ligne de commande et gérera la saisie semi-automatique?

En relation: /programming/7131670/make-bash-alias-that-takes-parameter

Ben Crowell
la source

Réponses:

15

Rediriger immédiatement l'erreur standard vers /dev/nullest une mauvaise idée car elle masquera les premiers messages d'erreur, et les échecs peuvent être difficiles à diagnostiquer. Je suggère quelque chose comme le start-appscript zsh suivant :

#!/usr/bin/env zsh
coproc "$@" 2>&1
quit=$(($(date +%s)+5))
nlines=0
while [[ $((nlines++)) -lt 10 ]] && read -p -t 5 line
do
  [[ $(date +%s) -ge $quit ]] && break
  printf "[%s] %s\n" "$(date +%T)" "$line"
done &

Il suffit de l'exécuter avec: start-app your_command argument ...

Ce script affichera au plus 10 lignes de messages et pendant au plus 5 secondes. Notez cependant que si l'application plante immédiatement (par exemple en raison d'un défaut de segmentation), vous ne verrez aucun message d'erreur. Bien sûr, vous pouvez modifier ce script de différentes manières pour faire ce que vous voulez ...

Remarque: Pour que les compléments fonctionnent avec start-appzsh, il suffit de faire:

compdef _precommand start-app

et en bash:

complete -F _command start-app

(copié de celui pour execet timededans /usr/share/bash-completion/bash_completion).

vinc17
la source
6
Idée mignonne, +1. Mais je ne suis pas d'accord que c'est une mauvaise idée en général de rediriger stderr depuis une application GUI. 99% de tous les utilisateurs l'invoqueront à partir d'un bureau graphique, de sorte qu'ils ne verront jamais rien qui soit allé à stderr. Le logiciel est conçu pour signaler les erreurs via l'interface graphique. Ce que vous voyez sur stdout et stderr est généralement le débogage de messages que les développeurs n'ont pas pris la peine de supprimer parce qu'ils ne pensaient pas que quiconque les verrait.
Ben Crowell
@BenCrowell J'accepte que les applications GUI signalent les erreurs via l'interface graphique, mais dans certains cas, il peut arriver que l'application échoue avant de démarrer l'interface graphique. Cela se produit en particulier lorsque l'application est invoquée via un script wrapper qui analyse les arguments (en général, ce n'est pas un problème pour les utilisateurs qui démarrent l'application à partir du bureau car dans ce cas, les arguments doivent être corrects).
vinc17
@BenCrowell Je pense aussi au cas où $DISPLAYn'est pas défini (par exemple si l'utilisateur a oublié un -Xpour ssh) ou à un problème d'autorisation X comme ici: unix.stackexchange.com/questions/108679/…
vinc17
@mikeserv Je pense que divers utilisateurs peuvent être intéressés par cette question (pas seulement l'OP), et ils peuvent utiliser bash ou zsh. Je viens d'ajouter une note pour les complétions en zsh et bash. Comme vous pouvez le voir, c'est simple.
vinc17
@mikeserv Notez qu'il y a un test à la date. Plus simple et plus portable, mais moins flexible si l'on veut ajouter des fonctionnalités: "$@" 2>&1 | { quit=$(($(date +%s)+5)); while read line && [ $(date +%s) -lt $quit ]; do printf "[%s] %s\n" "$(date +%T)" "$line"; done; } | head -n 10 &(le point le plus important était l'idée, pas la mise en œuvre réelle).
vinc17
5

Cette réponse est pour bash. Par exemple, voici ce que je fais dans mon .bashrc pour créer une commande pratique evpour démarrer la visionneuse PDF Evince.

ev() { (evince "$1" 1>/dev/null 2>/dev/null &) }
complete -f -o default -X '!*.pdf' ev

La première ligne définit une fonction ev. Le nom d'une fonction sera reconnu lorsque vous l'utiliserez sur la ligne de commande comme ceci:

ev foo.pdf

(Il s'agit d'un mécanisme différent de celui des alias, et il a une priorité inférieure.) La sortie d'Evince vers stdin et stdout est envoyée au bitbucket (/ dev / null). L'esperluette met le travail en arrière-plan. Entourer la commande entre parenthèses provoque son exécution dans un sous-shell afin qu'il n'imprime pas de messages sur la création du travail d'arrière-plan ou son achèvement.

La deuxième ligne de mon .bashrc utilise la fonction complète de bash pour dire à bash que l'argument de la commande ev devrait être un fichier avec l'extension pdf. Cela signifie que si j'ai également des fichiers foo.tex, foo.aux, etc., assis dans mon répertoire, je peux taper ev fooet appuyer sur la touche de tabulation, et bash saura compléter le nom de fichier en foo.pdf.

Ben Crowell
la source
1
Ben, juste pour que vous le sachiez, vous exagérez peut-être un peu la fonction. Aucune infraction ne signifiait - c'est une excellente réponse et j'ai été le premier à voter sur les questions et réponses, mais ... considérezev() (evince "$@" >&2 &) 2>/dev/null
mikeserv
Ouev() (evince "$@" &>/dev/null $)
Glenn Jackman
@glenn: Je pense que vous vouliez que l'avant-dernier personnage de votre suggestion soit a &.
G-Man dit `` Réintègre Monica ''
Oui, c'est vrai.
glenn jackman
5

Une autre possibilité est d'utiliser commandpour rétrograder execd'un builtin spécial à un vieux buildin simple comme:

alias shh='command exec >/dev/null 2>&1'

Alors maintenant, vous pouvez faire:

(shh; call some process &)

Je viens de remarquer que commandcela ne fonctionne pas zsh (comme cela semble le faire dans la plupart des autres shells) , mais où cela ne fonctionne pas, vous pouvez le faire à la place:

alias shh='eval "exec >/dev/null 2>&1"'

... qui devrait fonctionner partout.

En fait, vous pourriez même faire:

alias shh='command exec >"${O:-/dev/null}" 2>&1'

Vous pourriez donc faire:

O=./logfile; (shh;echo can anyone hear &)
O=; (shh; echo this\? &)
cat ./logfile

PRODUCTION

can anyone hear

Suite à une discussion de commentaires avec @ vinc17, il convient de noter que la quasi-totalité de la sortie de la console d'une application GUI est généralement destinée à Xtty - sa console. Lorsque vous exécutez une Xapplication à partir d'un X .desktopfichier, la sortie qu'elle génère est routée vers Xle terminal virtuel de - c'est-à-dire quel que soit le terminal à partir duquel vous avez lancé Xen premier lieu. Je peux adresser ce numéro tty avec $XDG_VTNR.

Curieusement cependant - et peut-être parce que je viens de commencer à utiliser startx- je n'arrive plus à écrire/dev/tty$XDG_VTNR . Cela peut également (comme je pense que c'est plus probable) avoir quelque chose à voir avec le changement très récent et radical implémenté avec la Xorgv1.16 qui lui permet de s'exécuter sous une systemdsession utilisateur plutôt que de nécessiter des privilèges root .

Pourtant, je peux faire:

alias gui='command exec >/dev/tty$((1+$XDG_VTNR)) 2>&1'

(gui; some x app &)

Maintenant tout some x app la sortie de la console est routée vers /dev/tty$((1+$XDG_VTNR))plutôt que la mienne xterm. Je peux obtenir la dernière page de ceci à tout moment comme:

fmt </dev/vcs$((1+$XDG_VTNR))

Il est probablement préférable de dédier un terminal virtuel à la sortie de journal de toute façon. /dev/consoleest généralement déjà réservé pour cela, bien que vous préfériez ne pas faire ce chownqui est probablement nécessaire pour que vous écriviez allègrement à cela. Vous pouvez avoir une fonction qui vous permet de faire un printk- qui est essentiellement une impression vers /dev/console- et pourrait donc l'utiliser de cette façon, je suppose.

Une autre façon de le faire serait de dédier un pty à ces fins. Vous pouvez, par exemple, garder une xtermfenêtre ouverte, enregistrer la sortie de ttylorsqu'elle est exécutée à partir de là dans une variable d'environnement et utiliser cette valeur comme destination pour guila sortie de. De cette façon, tous les journaux seraient acheminés vers une fenêtre de journal distincte, que vous pourriez ensuite parcourir si vous le souhaitez.

J'ai écrit une fois une réponse sur la façon dont une chose similaire pourrait être faite avec l' bashhistoire, si cela vous intéresse.

mikeserv
la source
1
Je vous suggère de supprimer votre remarque sur la sortie de echo $?car elle ajoute des informations inutiles et elle est basée sur un bogue dans bash, que je viens de signaler ici: lists.gnu.org/archive/html/bug-bash/2014- 08 / msg00081.html et dans le BTS Debian: bugs.debian.org/cgi-bin/bugreport.cgi?bug=758969
vinc17
@ vinc17 yup - je suppose que je dois avoir fait ça dans bash ce qui est bizarre - parce que je n'utilise jamais ce shell. suppose que je viens pour cette réponse.
mikeserv