Erzwingen eines 'hinzugefügten' Alias ​​zu jedem Befehl

11

Ist es möglich, jedem Befehl in zwangsweise einen Timing-Alias ​​hinzuzufügen (mangels einer besseren Formulierung) bash?

Zum Beispiel möchte ich einen bestimmten Benutzer haben, der bei jeder Ausführung eines Befehls immer entweder mit datevorher und nachher oder umbrochen wird time.

Ist das möglich und wenn ja, wie?

Labyrinth
quelle
Ich habe vorher nachgesehen und konnte keinen Weg finden, genau das zu tun. Wie Caleb sagt, können Sie es verwenden preexec, aber Sie möchten es nicht innerhalb von preexec(z. B. preexec() { time $1; }) ausführen , da die Shell es nach der preexecRückkehr immer noch ausführt. Das Beste, was wir tun können, ist etwas Ähnliches.
Mikel
@Mikel Ich denke, Sie könnten die preexec()Funktion verwenden, um das , was gerade ausgeführt wurde, tatsächlich zu verpacken , indem Sie den Befehl abrufen , ihn selbst aus der Funktion heraus ausführen und dann einen Fehler zurückgeben, damit die Shell den Befehl selbst nicht ausführt.
Caleb

Antworten:

10

Sie können die Zeit aufzeichnen, zu der eine Befehlszeile gestartet und eine Eingabeaufforderung angezeigt wird. Bash verfolgt bereits das Startdatum jeder Befehlszeile in ihrem Verlauf, und Sie können die Zeit notieren, zu der Sie die nächste Eingabeaufforderung anzeigen.

print_command_wall_clock_time () {
  echo Wall clock time: \
    $(($(date +%s) - $(HISTTIMEFORMAT="%s ";
                       set -o noglob;
                       set $(history 1); echo $2)))
}
PROMPT_COMMAND=print_command_wall_clock_time$'\n'"$PROMPT_COMMAND"

Dies gibt Ihnen nur eine zweite Auflösung und nur die Wanduhrzeit. Wenn Sie eine bessere Auflösung wünschen, müssen Sie einen externen dateBefehl verwenden, der das %NFormat für Nanosekunden und den DEBUGaufzurufenden Trap unterstütztdate bevor Sie den Befehl zur Zeit ausführen.

call_date_before_command () {
  date_before=$(date +%s.%N)
}
print_wall_clock_time () {
  echo Wall clock time: \
    $((date +"$date_before - %s.%N" | bc))
}
trap call_date_before_command DEBUG
PROMPT_COMMAND=print_command_wall_clock_time

Auch mit dem DEBUG Falle gibt es meines Erachtens keine Möglichkeit, die Prozessorzeiten für jeden Befehl automatisch anzuzeigen oder diskriminierender zu sein als die Aufforderung zur Aufforderung.


Wenn Sie bereit sind, eine andere Shell zu verwenden, erhalten Sie hier einen Zeitbericht für jeden Befehl in zsh (dies lässt sich nicht auf andere Aufgaben verallgemeinern):

REPORTTIME=0

Sie können einen REPORTTIMEbeliebigen ganzzahligen Wert festlegen. Die Timing-Informationen werden nur für Befehle angezeigt, die mehr als so viele Sekunden Prozessorzeit verbraucht haben.

Zsh hat diese Funktion von csh übernommen, wo die Variable aufgerufen wird time.

Gilles 'SO - hör auf böse zu sein'
quelle
3

Ihre Optionen hier hängen von Ihrer Shell ab. Dort zshgibt es eine praktische Hook-Funktion preexec(), die direkt vor allen interaktiven Shell-Befehlen ausgeführt wird. Durch das Erstellen einer Funktion mit diesem Namen können Sie die Ausführung von Dingen veranlassen. Sie können auch eine Funktion aufrufen, precmd()die unmittelbar vor dem Zeichnen der nächsten Eingabeaufforderung ausgeführt wird und direkt nach Beendigung Ihres Befehls ausgeführt wird.

Durch Erstellen dieses Funktionspaars können Sie beliebige Befehle ausführen lassen, bevor und nachdem Befehle an der Eingabeaufforderung ausgegeben werden. Sie können dies verwenden, um die Shell-Nutzung zu protokollieren, Sperren zu erstellen, die Umgebung zu testen oder wie in Ihrem Beispiel die Zeit oder Ressourcen zu berechnen, die während der Ausführung eines Befehls aufgewendet werden.

In diesem Beispiel erstellen wir uns einen Benchmark-Zeitstempel, bevor wir einen Befehl preexec()ausführen, precmd()und berechnen dann die Zeit, die für die Ausführung des Befehls aufgewendet wurde, und geben ihn vor der Eingabeaufforderung aus oder protokollieren ihn ab. Beispiel:

preexec() {
   CMDSTART=$(date +%s%N)
}
precmd() {
   CMDRUNTIME=$(($(date +%s%N)-$CMDSTART))
   echo "Last command ran for $CMDRUNTIME nanoseconds."
}

Hinweis: In diesem Beispiel gibt es eine noch einfachere integrierte Funktion. Alles, was Sie tun müssen, ist die Laufzeitberichterstattung in ZSH zu aktivieren, und dies wird automatisch durchgeführt.

$ export REPORTTIME=0
$ ls -d
./
ls -BF --color=auto -d  0.00s user 0.00s system 0% cpu 0.002 total

In einer praktischeren Implementierung von preexec()benutze ich es, um zu sehen, ob die Shell in tmuxoder läuftscreen um Informationen über den aktuell ausgeführten Befehl zu senden, die im Registerkartennamen angezeigt werden sollen.

Leider existiert dieser kleine Mechanismus in Bash nicht. Hier ist der Versuch eines Mannes, es zu replizieren . Siehe auch Gilles 'Antwort für einen ähnlichen kleinen Hack.

Caleb
quelle
1
Und hier ist eine vereinfachte Version des Precmd-Through-DEBUG-Tricks .
Gilles 'SO - hör auf böse zu sein'
Ich wünschte, dies wäre in Bash verfügbar!
Warren
Siehe Gilles 'und andere Links, es ist in Bash mit ein wenig mehr Fummelei implementierbar. Das nochmal, warum rennst du nicht einfach zsh? Es ist eine Schaukelschale mit mehr guten Gründen für einen Wechsel als nur diese!
Caleb
2
Wenn Sie zsh verwenden, gibt es einen noch besseren Weg, dies zu tun. Die Umgebungsvariable REPORTTIME gibt beim Festlegen die Ausführungszeitinformationen aus (als hätten Sie den Befehl mit 'time' davor ausgeführt) für jeden Befehl, der länger als $ REPORTTIME Sekunden dauert. Setzen Sie es einfach auf 0 und es sollte Ihnen die Zeit für jeden Befehl mit der Aufschlüsselung des Benutzers / Systems zum Booten anzeigen.
Joseph Garvin
1

Der einfachste Weg wäre wahrscheinlich zu setzen PROMPT_COMMAND. Siehe Bash-Variablen :

PROMPT_COMMAND
Wenn festgelegt, wird der Wert als Befehl interpretiert, der vor dem Drucken jeder primären Eingabeaufforderung ausgeführt werden soll ( $PS1).

Um beispielsweise das Überschreiben eines vorhandenen Eingabeaufforderungsbefehls zu vermeiden, können Sie Folgendes tun:

PROMPT_COMMAND="date ; ${PROMPT_COMMAND}"
cjm
quelle
wusste nicht darüber - sieht aus wie ein guter Start, @cjm
Warren
1
Es ist ein Anfang, geht aber nicht auf das Problem ein, zu wissen, wann ein Befehl ausgeführt wurde. Die Eingabeaufforderung selbst kann Minuten, Stunden oder Tage vor der Eingabe und Ausführung eines Befehls erstellt werden.
Caleb
0

cshIch tcshhabe die beste Unterstützung für diese Funktion (und hatte sie immer).

  The `time` shell variable can be set to execute the time builtin  command
  after the completion of any process that takes more than a given number
  of CPU seconds.

Mit anderen Worten, set time=1druckt die Zeit aus, die ein Befehl benötigt, der mehr als 1 Sekunde CPU-Zeit benötigt hat (System, Benutzer, verstrichen). Mit Plain set timekönnen Sie die Zeit für alle Befehle ausdrucken.

alexis
quelle