
Ich möchte eine Eingabeaufforderung schreiben PROMPT_COMMAND
, die auf alles reagiert, was in der Eingabeaufforderung unmittelbar zuvor angegeben wurde. Um beispielsweise zwischen einer ausführlichen, informativen Eingabeaufforderung und einer einfachen, kompakten Eingabeaufforderung zu wechseln, gehen Sie wie folgt vor:
mikemol@serenity ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$
mikemol@serenity ~ $
Werte scheinen nur dann zum Verlauf der Shell hinzugefügt zu werden, wenn sie nicht leer sind. Ich kann also nicht einfach testen, ob der letzte Verlaufseintrag leer ist. Ein Ausführen set | grep some_command
nach dem anderen some_command
gibt mir nichts, es scheint also keine Umgebungsvariable zu geben, die diese Informationen enthält.
Ich verwende hauptsächlich bash
, wäre aber an POSIX-kompatiblen Lösungen und anderen Shells interessiert.
Antwort1
Letztendlich brauchte ich PROMPT_COMMAND
das überhaupt nicht. Danke an Christopher, der mich in die richtige Richtung gelenkt hat.
Betrachten Sie stattdessen diese Datei ps1.prompt
:
${__cmdnbary[\#]+$(
echo '\u@\h: \w' # Your fancy prompt goes here, with all the usual special characters available.
) }${__cmdnbary[\#]=}\$
Ich kann dies dann in mein einfügen PS1
:
PS1=$(cat ps1.prompt)
(Sie müssen es nicht auf diese Weise machen, aber ich fand es zur Veranschaulichung und Bearbeitung praktisch.)
Und so sehen wir:
mikemol@zoe:~$ echo hi
hi
mikemol@zoe:~$ echo ho
ho
mikemol@zoe:~$ echo hum
hum
mikemol@zoe:~$
mikemol@zoe:~$ PS1=$(cat ps1.prompt)
$
mikemol@zoe: ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$
mikemol@zoe: ~ $
Wir verwenden den Array-Hackhier gezeigt, aber anstatt bash
die ${parameter:-word}
Parametersubstitution zu verwenden, verwenden wir ${parameter+word}
, sodass wir nur auslösen, wenn kein vorheriger Befehlslauf stattgefunden hat.
Dies bedarf einiger Erklärung, da wir in unserer Logik gezwungen sind, eine doppelte Verneinung zu verwenden.
Wie ${__cmdnbary[\#]-word}${__cmdnbary[\#]=}
funktioniert
In der ursprünglichen Demonstration des Array-Hacks ${__cmdnbary[\#]-word}${__cmdnbary[\#]=}
wurde die Konstruktion verwendet. (Der Übersichtlichkeit halber habe ich sie $?
durch ersetzt word
.) Wenn Sie mit der Parametererweiterung und Arrays nicht besonders vertraut sind (ich war es nicht), ist überhaupt nicht klar, was passiert.
Erstens: Verstehen Sie\#
Prodas Handbuch:
\#
die Befehlsnummer dieses Befehls
...
Die Befehlsnummer ist die Position in der Befehlssequenz, die während der aktuellen Shell-Sitzung ausgeführt wird.
Das bedeutet, \#
dass sich nurdann und nur dann, wennein Befehl wird ausgeführt. Wenn der Benutzer in der Eingabeaufforderung eine leere Zeile eingibt, wird kein Befehl ausgeführt und es kommt somit zu \#
keiner Änderung.
Das Setzen eines leeren Strings in ${__cmdnbary[#]=}
${__cmdnbary[\#]=}
verwendet Parametererweiterung. Zurück zudas Handbuch:
${parameter:=word}
Standardwerte zuweisen. Wenn der Parameter nicht gesetzt oder null ist, wird dem Parameter die Worterweiterung zugewiesen. Der Wert des Parameters wird dann ersetzt.
Wenn also __cmdnbary[\#]
nicht festgelegt oder null ist, weist diese Konstruktion eine leere Zeichenfolge zu ( word
ist in unserem Fall eine leere Zeichenfolge) und die gesamte Konstruktion wird in unserer Ausgabe durch dieselbe leere Zeichenfolge ersetzt.
__cmdnbary[\#]
Willestetssei beim ersten Mal nicht gesetzt oder null, da # monoton ist – es wird immer erhöht oder bleibt gleich. (Das heißt, bis es eine Schleife macht, wahrscheinlich bei etwa 2^31 oder 2^63, aber es gibt andere Probleme, die wir haben werdenlangbevor wir dort ankommen. Es gibt einen Grund, warum ich die Lösung als eine Art Hack bezeichne.)
Der Konditional in${__cmdnbary[\#]-word}
${__cmdnbary[\#]-word}
ist eine weitere Parametererweiterung. Aus dem Handbuch:
${parameter:-word}
Standardwerte verwenden. Wenn der Parameter nicht gesetzt oder null ist, wird die Worterweiterung ersetzt. Andernfalls wird der Wert des Parameters ersetzt.
Wenn also der Array-Eintrag \#
beinicht gesetzt oder null, wird an seiner Stelle verwendet. Da wir erst nach der Überprüfung versuchen, (mithilfe der Ersetzung) word
etwas zuzuweisen , sollte diese Position im Array beim ersten Überprüfen nicht festgelegt sein.__cmdnbary[\#]
${parameter:=word}
\#
bash
verwendet spärliche Arrays
Ein Punkt zur Klarstellung für diejenigen, die an Arrays im C-Stil gewöhnt sind. bash
verwendet tatsächlichspärliche Arrays; bis Sie zuweisenetwasan eine Position in einem Array, ist diese Position nicht gesetzt. Eine leere Zeichenfolge ist nicht dasselbe wie „null oder nicht gesetzt“.
Warum wir stattdessen ${__cmdnbary[#]+word}${__cmdnbary[#]=} verwenden
${__cmdnbary[\#]+word}${__cmdnbary[\#]=}
und ${__cmdnbary[#]-word}${__cmdnbary[#]=} look very siilar. The *only* thing we change between the two constructs can be found in the first portion; we use
${parameter:+word} instead of
${parameter:-word}`.
Denken Sie daran ${parameter:-word}
, dass mit word
nur angezeigt wird, wenn parameter
null oder nicht gesetzt ist - in unserem Fall nur, wenn wirnichtLegen Sie die Position im Array noch nicht fest. Dies haben wir genau dann nicht getan, wenn \#
inkrementiert wurde, was nur passiert, wenn wir gerade einen Befehl ausgeführt haben.
Das bedeutet, dass ${parameter:-word}
wir mit nur präsentieren word
, wenn wirnichthat einen Befehl ausgeführt, der genau das Gegenteil von dem ist, was wir tun möchten. Also verwenden wir ${parameter:-word}
stattdessen. Nochmals aus dem Handbuch:
${parameter:+word}
Alternativwert verwenden. Wenn der Parameter null oder nicht gesetzt ist, wird nichts ersetzt, andernfalls wird die Worterweiterung ersetzt.
Das ist (leider) noch mehr doppelt-verneinte Logik, die schwer zu verstehen ist, aber so ist es nun einmal.
Die Eingabeaufforderung selbst
Wir haben den Umschaltmechanismus erklärt, aber was ist mit der Eingabeaufforderung selbst?
Hier verwende ich , $( ... )
um das Wesentliche der Eingabeaufforderung einzuschließen. Hauptsächlich zu meinem eigenen Vorteil hinsichtlich der Lesbarkeit; Sie müssen es nicht so machen. Sie können es $( ... )
durch alles ersetzen, was Sie normalerweise in eine Variablenzuweisung stopfen würden.
Warum ist es ein Hack?
Erinnern Sie sich, wie wir Einträge zu einem spärlichen Array hinzufügen? Wir entfernen diese Einträge nicht, und so wird das Array für immer wachsen, bis die Shell-Sitzung beendet wird; die Shell sickert durch PS1
. Und soweit ich weiß, gibt es keine Möglichkeit,nicht gesetzteine Variablen- oder Arrayposition in der Eingabeaufforderung. Sie könnten es in versuchen $()
, aber Sie werden feststellen, dass es nicht funktioniert. Änderungen am Variablen-Namespace innerhalb einer Subshell werden nicht auf den Bereich angewendet, aus dem die Subshell abgezweigt wurde.
Dukönnteversuchen Sie es mktmp
früh in Ihrem .bashrc
, vor PS1
der Zuweisung, und stopfen Sie die Informationen in die resultierende Datei. Dann können Sie Ihre aktuelle Datei \#
mit dem vergleichen, was Sie dort gespeichert haben, aber jetzt haben Sie Ihre Eingabeaufforderung vom Festplatten-E/A abhängig gemacht, was eine gute Möglichkeit ist, sich in Notsituationen selbst auszusperren.