Wie vermeide ich Störungen beim Starten einer grafischen Benutzeroberfläche von einem Terminal aus?

14

Ich starte GUI-Anwendungen lieber über ein Terminalfenster als über einen grafischen Desktop. Ein häufiges Ärgernis ist, dass die Entwickler diese Art der Verwendung oft nicht erwartet haben, sodass die App viele nutzlose, kryptische oder nicht informative Nachrichten an stdout oder stderr druckt. Weitere Unordnung auf dem Terminal tritt auf, weil das Ausführen des Programms im Hintergrund mit einem & Berichte über die Erstellung und Beendigung des Jobs generiert.

Was ist eine Problemumgehung für diese Probleme, die Befehlszeilenargumente akzeptieren und die automatische Vervollständigung verarbeiten?

Verwandte Themen: /programming/7131670/make-bash-alias-that-takes-parameter

Ben Crowell
quelle

Antworten:

15

Das sofortige Umleiten des Standardfehlers auf /dev/nullist eine schlechte Idee, da dadurch frühzeitige Fehlermeldungen ausgeblendet werden und Fehler möglicherweise schwer zu diagnostizieren sind. Ich schlage folgendes start-appzsh-Skript vor:

#!/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 &

Führen Sie es einfach mit: start-app your_command argument ...

Dieses Skript gibt höchstens 10 Nachrichtenzeilen und höchstens 5 Sekunden lang aus. Beachten Sie jedoch, dass bei einem sofortigen Absturz der Anwendung (z. B. aufgrund eines Segmentierungsfehlers) keine Fehlermeldung angezeigt wird. Natürlich können Sie dieses Skript auf verschiedene Arten ändern, um das zu tun, was Sie wollen ...

Hinweis: Damit Vervollständigungen start-appin zsh funktionieren , genügt Folgendes:

compdef _precommand start-app

und in bash:

complete -F _command start-app

(kopiert von dem für execund timein /usr/share/bash-completion/bash_completion).

vinc17
quelle
6
Nette Idee, +1. Ich bin jedoch anderer Meinung, dass es im Allgemeinen eine schlechte Idee ist, stderr von einer GUI-App umzuleiten. 99% aller Benutzer rufen es von einem grafischen Desktop aus auf, sodass sie nie etwas sehen, das an stderr geht. Die Software wurde entwickelt, um Fehler über die GUI zu melden. Was Sie auf stdout und stderr sehen, ist normalerweise das Debuggen von Nachrichten, die die Entwickler nicht herausgenommen haben, weil sie nicht dachten, dass jemand sie sehen würde.
Ben Crowell
@BenCrowell Ich stimme zu, dass GUI-Apps Fehler über die GUI melden sollten. In einigen Fällen kann es jedoch vorkommen, dass die Anwendung fehlschlägt, bevor die GUI gestartet wird. Dies tritt insbesondere dann auf, wenn die Anwendung über ein Wrapper-Skript aufgerufen wird, das die Argumente analysiert (dies ist im Allgemeinen kein Problem für Benutzer, die die App vom Desktop aus starten, da in diesem Fall die Argumente korrekt sein sollten).
Vinc17
@BenCrowell Ich denke auch an den Fall, wo $DISPLAYnicht gesetzt ist (zB wenn der Benutzer ein -Xfür ssh vergessen hat ) oder ein X-Berechtigungsproblem wie hier: unix.stackexchange.com/questions/108679/…
vinc17
@mikeserv Ich denke, dass verschiedene Benutzer an dieser Frage interessiert sein könnten (nicht nur das OP), und sie können entweder bash oder zsh verwenden. Ich habe gerade eine Notiz für die Vervollständigungen in zsh und bash hinzugefügt. Wie Sie sehen können, ist dies einfach.
Vinc17
@mikeserv Beachten Sie, dass am Datum ein Test stattfindet. Einfacher und portabler, aber weniger flexibel, wenn man Features hinzufügen möchte: "$@" 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 &(Der wichtigste Punkt war die Idee, nicht die tatsächliche Implementierung).
Vinc17
5

Diese Antwort ist für die Bash. Als Beispiel hier ist, was ich in meinem .bashrc mache, um einen Bequemlichkeitsbefehl evzum Starten des PDF-Viewers Evince zu erstellen .

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

Die erste Zeile definiert eine Funktion ev. Der Name einer Funktion wird erkannt, wenn Sie sie in der Befehlszeile wie folgt verwenden:

ev foo.pdf

(Dies ist ein anderer Mechanismus als Aliase und hat eine niedrigere Priorität.) Die Ausgabe von Evince an stdin und stdout wird an das Bitbucket (/ dev / null) gesendet. Das kaufmännische Und stellt den Job in den Hintergrund. Wenn Sie den Befehl in Klammern setzen, wird er in einer Subshell ausgeführt, sodass keine Nachrichten über die Erstellung oder den Abschluss des Hintergrundjobs gedruckt werden.

In der zweiten Zeile von .bashrc wird die vollständige Funktion von bash verwendet, um bash mitzuteilen, dass das Argument des Befehls ev voraussichtlich eine Datei mit der Erweiterung pdf ist. Dies bedeutet, dass ich, wenn ich auch Dateien foo.tex, foo.aux usw. in meinem Verzeichnis habe, ev foodie Tabulatortaste eingeben und drücken kann und bash weiß, dass der Dateiname als foo.pdf vervollständigt wird.

Ben Crowell
quelle
1
Ben, nur damit du weißt, dass du die Funktion vielleicht ein wenig übertreibst. Nichts für ungut gemeint - es ist eine großartige Antwort und ich war der erste, der Fragen und Antworten positiv bewertet hat, aber ... ev() (evince "$@" >&2 &) 2>/dev/null
denken Sie daran
Oderev() (evince "$@" &>/dev/null $)
Glenn Jackman
@glenn: Ich glaube, dass du für den vorletzten Charakter in deinem Vorschlag gedacht hast, ein &.
G-Man sagt, dass Monica
Ja ganz richtig
Glenn Jackman
5

Eine weitere Möglichkeit ist die Verwendung commandherabzuzustufen execvon einem speziellen builtin zu einer einfachen alten builtin wie:

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

Jetzt können Sie also Folgendes tun:

(shh; call some process &)

Ich habe gerade bemerkt, dass commanddas nicht funktioniert zsh (wie es in den meisten anderen Shells der Fall zu sein scheint) , aber wo es nicht funktioniert, können Sie stattdessen Folgendes tun:

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

... die überall funktionieren sollte.

In der Tat könnten Sie sogar tun:

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

Sie könnten also Folgendes tun:

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

AUSGABE

can anyone hear

Nach einer Kommentardiskussion mit @ vinc17 ist anzumerken, dass fast die gesamte Konsolenausgabe einer GUI-App im Allgemeinen für Xdie tty bestimmt ist - die Konsole. Wenn Sie eine XApp aus einer X .desktopDatei ausführen, wird die von ihr erzeugte Ausgabe an Xdas virtuelle Terminal weitergeleitet - unabhängig davon, von welchem ​​Zeitpunkt aus Sie sie gestartet Xhaben. Ich kann diese tty Nummer mit adressieren $XDG_VTNR.

Seltsamerweise - und vielleicht, weil ich gerade angefangen habe zu benutzen startx- kann ich nicht länger nur schreiben /dev/tty$XDG_VTNR. Dies kann auch (wie ich es für wahrscheinlicher halte) mit der jüngsten und drastischen Änderung zu tun haben, die mit XorgVersion 1.16 implementiert wurde und die es ermöglicht, unter einer systemdBenutzersitzung ausgeführt zu werden, anstatt Root- Berechtigungen zu erfordern .

Trotzdem kann ich:

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

(gui; some x app &)

Jetzt wird die gesamte some x appKonsolenausgabe an /dev/tty$((1+$XDG_VTNR))statt an meine xtermPty weitergeleitet. Ich kann die letzte Seite davon jederzeit wie folgt abrufen:

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

Es wird wahrscheinlich empfohlen, ein virtuelles Terminal zu verwenden, um die Ausgabe trotzdem zu protokollieren. /dev/consoleist in der Regel bereits dafür reserviert, obwohl Sie es vielleicht vorziehen, nicht das zu tun chown, was Sie wahrscheinlich benötigen, um munter darauf zu schreiben. Möglicherweise verfügen Sie über eine Funktion, mit der Sie eine Funktion ausführen können, für printkdie im Grunde genommen gedruckt wird, /dev/consoleund die Sie möglicherweise auf diese Weise verwenden können.

Ein anderer Weg, dies zu tun, wäre, ein Pty solchen Zwecken zu widmen . Sie können beispielsweise ein xtermFenster geöffnet lassen, die Ausgabe ttyvon dort in einer Umgebungsvariablen speichern und diesen Wert als Ziel für guidie Ausgabe verwenden. Auf diese Weise würden alle Protokolle in ein separates Protokollfenster geleitet, in dem Sie bei Bedarf einen Bildlauf durchführen können.

Ich habe einmal eine Antwort darüber geschrieben, wie man etwas Ähnliches mit der bashGeschichte machen kann, wenn man interessiert ist.

mikeserv
quelle
1
Ich schlage vor, dass Sie Ihre Bemerkung in der Ausgabe von entfernen, echo $?da sie nutzlose Informationen hinzufügt und auf einem Bash-Fehler basiert, den ich gerade hier gemeldet habe: lists.gnu.org/archive/html/bug-bash/2014- 08 / msg00081.html und in der Debian-BTS: bugs.debian.org/cgi-bin/bugreport.cgi?bug=758969
vinc17
@ vinc17 yup - ich denke, ich muss das in bash gemacht haben, was komisch ist - weil ich diese Shell nie benutze. denke ich nur für diese Antwort.
mikeserv