Wie man einen Befehl so täuscht, dass er denkt, dass seine Ausgabe an ein Terminal geht

38

Wie kann bei einem Befehl, der sein Verhalten ändert, wenn seine Ausgabe an ein Terminal gesendet wird (z. B. Ausgabe in Farbe), diese Ausgabe in einer Pipeline umgeleitet werden, während das geänderte Verhalten beibehalten wird? Dafür muss es einen Nutzen geben, den ich nicht kenne.

Einige Befehle grep --color=alwayshaben Optionsflags, um das Verhalten zu erzwingen, aber die Frage ist, wie Programme umgangen werden können, die ausschließlich auf dem Testen ihres Ausgabedateideskriptors beruhen.

Wenn es darauf ankommt, läuft meine Shell bashunter Linux.

Amir
quelle
Sie müssen wissen, wie der Befehl seinen Ausgabedateideskriptor testet und irgendwie Ergebnisse zurückgibt, die mit den Ergebnissen übereinstimmen, die von einem Ausgabeterminal angezeigt würden.
Andrew Henle

Antworten:

21

Sie erhalten möglicherweise, was Sie benötigen, indem Sie verwenden unbuffer.

unbufferist ein tcl/ expectscript. Schauen Sie sich die Quelle an, wenn Sie möchten. Beachten Sie auch den Abschnitt CAVEATS in man.

Beachten Sie auch, dass keine Aliase wie die folgenden ausgeführt werden:

alias ls='ls --color=auto'

es sei denn, man fügt einen von Stéphane Chazelas angegebenen Trick hinzu:

Wenn Sie a machen alias unbuffer='unbuffer '(beachten Sie das nachstehende Leerzeichen), werden die Aliase danach erweitert unbuffer.

Runium
quelle
Hinweis zu anerkannten Aliasen - Es funktioniert auch nicht mit anderen Shell-Konstrukten wie Builtins und Funktionen.
Amir
4
Wenn Sie a machen alias unbuffer='unbuffer '(beachten Sie das nachstehende Leerzeichen), werden die Aliase danach erweitert unbuffer.
Stéphane Chazelas
unbufferes ist! sudo apt install expect- Das war unklar.
Loxax
22

Eine Geschichte von Werkzeugen

Sie sind nicht die erste Person, die ein solches Werkzeug haben möchte. Seit 30 Jahren wollen die Leute solche Werkzeuge. Und die gibt es auch schon fast so lange.

Das früheste Werkzeug für diese Art von Dingen war Daniel J. Bernsteins "Pty" -Paket, das von Rich Salz als "Ginsu-Messer" beschrieben wurde und das Bernstein um die Wende der 1990er Jahre schrieb, um Nethack zu betrügen (sic!). Version 4 des Pakets "pty" wurde 1992 veröffentlicht comp.sources.unix(Band 25, Ausgaben 127 bis 135). Es ist immer noch im World Wide Web zu finden. Paul Vixie beschrieb es damals:

Was kann ich sagen? Es schneidet, es würfelt, es wäscht Geschirr, es geht mit dem Hund spazieren. Es "funktioniert einfach", was bedeutet, dass Sie, wenn Sie den Anweisungen folgen, ein Arbeitspaket erhalten, ohne an den Haaren zu ziehen oder an den Zähnen zu knirschen oder andere übliche Portierungsaktivitäten durchzuführen.

Bernstein aktualisierte dies später, irgendwann am oder vor dem 07.04.1999, mit einem "ptyget" -Paket, das er ankündigte:

Ich habe einen neuen Pseudo-Tty-Allokator zusammengestellt, ptyget. Eine Alpha-Version ist bei ftp://koobera.math.uic.edu/pub/software/ptyget-0.50.tar.gz. Es gibt eine Ptyget-Mailingliste. Senden Sie zum Beitritt eine leere Nachricht an [email protected]. Ich habe die Oberfläche von ptyget von Grund auf neu gestaltet. Es ist viel modularer als pty; Das grundlegende Pty-Interface wurde nun in drei Teile aufgeteilt:

  • ptyget: ein winziges Programm auf niedriger Ebene - das einzige Setuid-Programm im Paket -, das eine neue Pseudotty zuweist und an das Programm Ihrer Wahl weitergibt
  • ptyspawn: Ein weiteres kleines Programm, das einen untergeordneten Prozess unter einer Pseudotty ausführt, darauf wartet, dass er beendet wird und auf Stopps wartet
  • ptyio: Ein weiteres, nur geringfügig größeres Programm, das Daten hin und her bewegt

Das alte Ginsu-Messer ptyist jetzt buchstabiert ptybandage, ein Synonym für ptyget ptyio -t ptyspawn; pty -d, um Netzwerkprogramme an pseudo-ttys anzuhängen, wird jetzt buchstabiert ptyrun, was synonym ist für ptyget ptyio ptyspawn; und nobufist ein Synonym für ptyget ptyio -r ptyspawn -23x. Ich habe die Sitzungsverwaltungsfunktionen in ein separates Paket aufgeteilt.

Dieses separate Paket war das "Sess" -Paket.

"ptyget" ist übrigens ein Beispiel für eine sehr frühe Version von Bersteins "Redo" -Build-System und eine der wenigen veröffentlichten Instanzen. dependonist ein klarer Vorläufer von redo-ifchange.

Verwendung

ptybandage

ptybandageist, was Leute normalerweise in einer LOGON-Sitzung wünschen. Der primäre Anwendungsfall besteht darin, Programme so zu konfigurieren, dass sie empfindlich darauf reagieren, ob ihre Standardeingaben, -ausgaben oder -fehler mit Terminals verbunden sind, obwohl sie sich in Shell-Pipelines befinden oder ihre Standarddateideskriptoren in eine Datei umgeleitet werden.

Zum Ausführen wird ein Befehl benötigt (dies muss natürlich ein ordnungsgemäßer externer Befehl sein), und der Befehl wird so ausgeführt, dass er denkt, dass seine Standardeingabe, -ausgabe und -fehler an ein Terminal angeschlossen sind, das diese mit ptybandage's' verbindet ursprüngliche Standardeingabe, Ausgabe und Fehler.

Es befasst sich mit den Nuancen der Ausführung unter Jobsteuerungs-Shells, wobei sichergestellt wird, dass ein Terminal-STOP-Zeichen nicht nur stoppt, ptybandagesondern auch das Programm anhält, das mit dem inneren Terminal verbunden ist.

ptyrun

ptyrunist das, was die Leute normalerweise von TCP-Netzwerkservern erwarten. Der primäre Anwendungsfall sind Remote-Ausführungsumgebungen, in denen keine Terminals eingerichtet sind und Programme ausgeführt werden, die nicht wie gewünscht funktionieren, wenn kein Terminal vorhanden ist.

Es wird nicht erwartet, dass es unter einer Jobsteuerungs-Shell ausgeführt wird, und wenn der ausgeführte Befehl ein Stoppsignal empfängt, wird er einfach neu gestartet.

Verfügbare Toolsets

Dru Nelson veröffentlicht sowohl "pty" Version 4 als auch "ptyget".

Paul Jarc veröffentlicht eine feste Version von ptyget, die versucht, die betriebssystemspezifischen Pseudo-Endgeräte-Ioctls im Original zu behandeln, die Betriebssysteme eigentlich nicht mehr bereitstellen.

Das nosh-Quellpaket wird mit Workalike ptybandangeund ptyrunSkripten geliefert , die das execlineTool von Laurent Bercot und die eigenen Pseudo-Terminal-Verwaltungsbefehle des nosh-Pakets verwenden. Ab der nosh-Version 1.23 sind diese im nosh-terminal-extras-Paket enthalten. (Frühere Versionen haben sie nur an Personen geliefert, die aus dem Quellcode erstellt haben.)

Ein paar Anwendungsbeispiele

Jurjgen Oskam verwendet ptybandageunter AIX , um Eingaben aus einem Here-Dokument in ein Programm einzugeben, das explicity öffnet, und liest sein steuerndes Terminal für eine Kennwortabfrage:

$ ptybandage dsmadmc << EOF> uit.txt
joskam
Passwort
Abfragesitzung
Abfrageprozess
Verlassen
EOF

Andy Bradford benutzt ptyrunOpenBSD unter daemontools und ucspi-tcp, um das bgplgshinteraktive Router-Steuerungsprogramm über das Netzwerk zugänglich zu machen, während er denkt, dass es mit einem Terminal spricht:

#! / bin / sh
exec 2> & 1
exec envuidgid rviews tcpserver -vDRHl0 0 23 ptyrun / usr / bin / bgplgsh

Weitere Lektüre

JdeBP
quelle
Vielen Dank für die Lektion in der Geschichte, aber die von Ihnen vorgeschlagenen Tools sind in einer modernen Linux-Distribution nicht verfügbar und daher nicht nützlich.
Amir
4
Es gibt eine Reihe von Hyperlinks und ein ganzer Abschnitt der Antwort gewidmet, wo die Werkzeuge ganz sicher sind verfügbar.
JdeBP
1
Was ist deine Meinung dazu expect?
CMCDragonkai
20

Sie können mit socat Ihren Prozess mit einem beginnen pty verbunden und bekommen socat das andere Ende des pty in eine Datei zu verbinden. Welche AFAIU ist genau das, was Sie gefragt haben:

socat EXEC:"my-command",pty GOPEN:mylog.log

Diese Methode führt dazu, dass der isattyAufruf von my-commandzurückgegeben wird, trueund ein Prozess, der sich nur darauf stützt, wird getäuscht, um Steuercodes auszugeben. Beachten Sie, dass einige Prozesse (insbesondere grep) auch den Wert der TERMUmgebungsvariablen überprüfen , sodass Sie ihn möglicherweise auf einen vernünftigen Wert wie festlegen müssen"xterm"

Guss
quelle
13

Es gibt auch eine nette Lösung, die hier auf Super User von KarlC gepostet wird :

Kompilieren Sie eine kleine gemeinsam genutzte Bibliothek:

echo "int isatty(int fd) { return 1; }" | gcc -O2 -fpic -shared -ldl -o isatty.so -xc -

Dann weisen Sie Ihren Befehl an, diese isatty(3)Überschreibung dynamisch zu laden :

LD_PRELOAD=./isatty.so mycommand

Dies funktioniert möglicherweise nicht für jeden Befehl, kann jedoch in den meisten Fällen zu unerwarteten Fehlern führen.

Amir
quelle
2
Für MacOS-Benutzer können Sie dasselbe Verhalten DYLD_INSERT_LIBRARIES=./isatty.so DYLD_FORCE_FLAT_NAMESPACE=y mycommand
erzielen,
12

Wie wäre es mit script(1)?

Beispielsweise:

script -q -c 'ls -G' out_file

Speichert die lsAusgabe out_fileunter Beibehaltung der Farbcodes.

sagi
quelle
Das geht hier nicht. Wie bleiben Farben erhalten? Gibt es ein Tool, mit dem ich out_filedie Farben ausgeben soll ?
Kira
1
@Kira zum Anzeigen von Dateien mit ANSI-Farb-Escape-Sequenzen benutze ich less -R. In diesem Fall wollte ich jedoch, dass die Ausgabe in der Pipeline fortgesetzt wird, die schließlich in meinem Terminal landete. Zur catVeranschaulichung wurde so etwas verwendet script -q -c 'ls -G' /dev/null | cat, das die typescriptDatei vollständig unterdrückt und nur die Ausgabe des Programms zurücklässt.
Amir
Um das Erstellen einer Datei zu vermeiden, verwenden Sie einfach einen Gedankenstrich ( -) als scriptAusgabedatei, zum Beispiel:script -q -c 'ls -G' -
Franklin Piat
0

Basierend auf der Antwort von @ Amir ist hier ein Skript, das die Bibliothek zur Laufzeit generiert und dann einschließt:

#!/bin/bash
set -euo pipefail

function clean_up {
  trap - EXIT # Restore default handler to avoid recursion
  [[ -e "${isatty_so:-}" ]] && rm "$isatty_so"
}
# shellcheck disable=2154 ## err is referenced but not assigned
trap 'err=$?; clean_up; exit $err' EXIT HUP INT TERM

isatty_so=$(mktemp --tmpdir "$(basename "$0")".XXXXX.isatty.so)
echo "int isatty(int fd) { return 1; }" \
  | gcc -O2 -fpic -shared -ldl -o "$isatty_so" -xc -
# Allow user to SH=/bin/zsh faketty mycommand
"${SH:-$SHELL}" -c 'eval $@' - LD_PRELOAD="$isatty_so" "$@"
Tom Hale
quelle