Wie kann der Befehlsverlauf mit Zeitstempeln kontinuierlich an das Terminal ausgegeben werden?


8

Ich verwende einen einfachen Alias , um die "Verfolgung" von Befehlen in einem oder mehreren Terminalfenstern zu ermöglichen:

alias trackmi='export PROMPT_COMMAND="history -a; $PROMPT_COMMAND"'

Dann habe ich nur tail -fmeine .bash_history- Datei in einem anderen Terminal im Arbeitsbereich, um sofortiges Feedback zu erhalten. Ich habe gerade den unbegrenzten Verlauf aktiviert und mein Verlaufsformat ( export HISTTIMEFORMAT="[%F %T] ") in .bashrc aktualisiert . Natürlich historyzeigt der Befehl die Zeitstempel an. Das Format der Verlaufsdatei an sich ist jedoch:

#1401234303
alias
#1401234486
cat ../.bashrc 

Wie kann ich die Unix-Zeit konvertieren und den gesamten Befehl in einer einzelnen Zeile anzeigen lassen, genau wie beim historyBefehl, einschließlich der Nummerierung:

578  [2014-05-27 19:45:03] alias
579  [2014-05-27 19:48:06] cat ../.bashrc 

... und folge dem. Oder einen Weg finden, die Ausgabe des historyBefehls kontinuierlich an das Terminal auszugeben ?

Antworten:


6

Mit GNU awk:

tail -fn+1 ~/.bash_history | awk '
  /^#/{printf "%-4d [%s] ", ++n, strftime("%F %T", substr($0, 2)); next}; 1'

Funktioniert hervorragend und wird anfangs schneller angezeigt, wenn ich die andere Lösung fn+1zum Vergleichen aktualisiere ! Vielen Dank!

5

Hier ist das Endprodukt in Aktion auf einem Split-Screen-Xterm, von den Shell-Standardeinstellungen bis hin zur Arbeit mit nur wenigen Befehlen:

Geben Sie hier die Bildbeschreibung ein

Ein rauerer Weg, dies zu tun, als im Screenshot gezeigt, könnte folgendermaßen aussehen:

PS1='$( { date ; fc -l -0 ; } >${TGT_PTY} )'$PS1

Wo ${TGT_PTY}wäre das, was Sie aus dem ttyBefehl herausholen, wenn Sie tatsächlich eine interaktive Shell auf dem Bildschirm ausführen, auf dem Sie Ihre Ausgabe wünschen. Sie können auch jede beschreibbare Datei verwenden, da sie im Wesentlichen nur ein Ziel für die Umleitung von Dateien ist.

Ich verwende die pty- Syntax für Pseudo-Terminals, weil ich davon ausgehe, dass es sich um ein xterm handelt, aber Sie können genauso gut ein vt zuweisen - und Ihr gestreamter Verlauf ist immer nur eine CTRL-ALT-FnTastenkombination entfernt. Wenn ich es wäre, könnte ich die beiden Begriffe kombinieren und es zu einer screenoder einer tmuxSitzung auf einem dedizierten vt machen ... Aber ich schweife ab.

Auf einem frisch gebooteten Computer werde ich mit der typischen /bin/loginEingabeaufforderung auf einer typischen Linux- gettyKonsole begrüßt . Ich drücke, CTRL-ALT-F2um auf eine weniger typische kmsconKonsole zuzugreifen, die sich viel mehr wie eine xtermals eine verhält tty. Ich gebe den Befehl ein ttyund erhalte als Antwort /dev/pts/0.

Im Allgemeinen multiplexen xterms ein einzelnes Endgerät mithilfe von Pseudo-Terminals in mehrere. Wenn Sie also in X11 etwas Ähnliches tun, indem Sie zwischen Terminal-Registerkarten oder Fenstern wechseln, erhalten Sie wahrscheinlich auch eine Ausgabe wie diese /dev/pts/[0-9]*. Die virtuellen Terminalkonsolen, auf die mit CTRL-ALT-FnTastenkombinationen zugegriffen wird, sind jedoch echte (er) Endgeräte und erhalten daher ihre eigene /dev/tty[0-9]*Bezeichnung.

Dies ist der Grund, warum nach der Anmeldung bei Konsole 2, wenn ich ttyan der Eingabeaufforderung tippe, die Antwort lautet, /dev/pts/0aber wenn ich dasselbe auf Konsole 1 mache, ist die Ausgabe /dev/tty1. Auf jeden Fall mache ich dann wieder auf Konsole 2:

bash
PS1='$( { date ; fc -l -0 ; } >/dev/tty1 )'$PS1

Es ist kein Effekt erkennbar. Ich tippe noch ein paar Befehle ein und wechsle dann durch erneutes Drücken zu Konsole 1 CTRL-ALT-F1. Und dort finde ich wiederholte Einträge, die <date_time>\n<hist#>\t<hist_cmd_string>für jeden Befehl aussehen, den ich auf Konsole 2 eingegeben habe.

Wenn Sie nicht direkt auf ein Endgerät schreiben, könnte eine andere Option folgendermaßen aussehen:

TGT_PTY=
mkfifo ${TGT_PTY:=/tmp/shell.history.pipe}
{   echo 'OPENED ON:'
    date
} >${TGT_PTY}

Und dann vielleicht ...

less +F ${TGT_PTY}

Der grobe Eingabeaufforderungsbefehl entspricht nicht Ihren Spezifikationen - keine Formatzeichenfolge für dateund auch keine Formatierungsoptionen fc-, aber sein Mechanismus erfordert nicht viel: Jedes Mal, wenn Ihre Eingabeaufforderung den letzten Verlaufsbefehl ausgibt und das aktuelle Datum und die aktuelle Uhrzeit ausgeschrieben werden die von ${TGT_PTY}Ihnen angegebene Datei. So einfach ist das.

Das Beobachten und Drucken des Shell-Verlaufs ist fcder Hauptzweck. Es ist eine eingebaute Shell, auch wenn dies datenicht der Fall ist. In zsh fckann alle Arten von ausgefallenen Formatierungsoptionen bereitstellen, von denen einige für Zeitstempel gelten. Und natürlich, wie Sie oben beachten, bash‚s historykann das gleiche tun.

Im Interesse einer saubereren Ausgabe können Sie eine hier besser erläuterte Technik verwenden , um eine persistente Verfolgungsvariable in der aktuellen Shell festzulegen, obwohl Sie sie verfolgen und in Unterschalen innerhalb der Eingabeaufforderungssequenz verarbeiten müssen.

Hier ist ein tragbares Mittel zum Formatieren nach Ihren Vorgaben:

_HIST() { [ -z ${_LH#$1} ] ||
    { date "+${1}%t[%F %T]"
      fc -nl -0 
    } >${TGT_PTY}
    printf "(_LH=$1)-$1"
}

: "${_LH=0}"
PS1='${_LH##*[$(($(_HIST \!)))-9]}'$PS1

Ich implementiere den Zähler last_history$_LH , der nur die neuesten Aktualisierungen verfolgt, damit Sie nicht zweimal denselben Verlaufsbefehl ausschreiben - zum Beispiel nur, indem Sie die Eingabetaste drücken. Es ist ein wenig Streit erforderlich, um die Variable in der aktuellen Shell zu erhöhen, damit sie ihren Wert behält, obwohl die Funktion in einer Subshell aufgerufen wird - was wiederum im Link besser erklärt wird .

Die Ausgabe sieht aus wie <hist#>\t[%F %T]\t<hist_cmd>\n

Aber das ist nur die voll portable Version. Mit bashnur Schal builtins mit weniger und durch die Implementierung durchgeführt werden kann - und das ist wahrscheinlich wünschenswert , wenn man bedenkt , dass dies ein Befehl ist , dass jedes Mal , wenn Sie laufen drücken [ENTER]. Hier sind zwei Möglichkeiten:

_HIST() { [ -z ${_LH#$1} ] || {
        printf "${1}\t[%(%F %T)T]"
        fc -nl -0
    } >${TGT_PTY}
    printf "(_LH=$1)-$1"
}
PROMPT_COMMAND=': ${_LH=0};'$PROMPT_COMMAND
PS1='${_LH##*[$(($(_HIST \!)))-9]}'$PS1

Alternativ können Sie mit bashdem historyBefehl 's die _HISTFunktion folgendermaßen definieren :

_HIST() { [ -z ${_LH#$1} ] || 
        HISTTIMEFORMAT="[%F %T]<tab>" \
        history 1 >${TGT_PTY}
    printf "(_LH=$1)-$1"
}

Die Ausgabe für beide Methoden sieht ebenfalls so aus: <hist#>\t[%F %T]\t<hist_cmd>\nDie historyMethode enthält jedoch einige führende Leerzeichen. Trotzdem glaube ich, dass die historyZeitstempel der Methode genauer sind, da ich nicht glaube, dass sie warten müssen, bis der Befehl, auf den verwiesen wird, abgeschlossen ist, bevor sie ihren Stempel erhalten.

Sie können in beiden Fällen vermeiden, einen Status zu verfolgen, wenn Sie den Stream nur irgendwie filtern uniq- wie Sie es vielleicht tun, mkfifowie ich bereits erwähnt habe.

Wenn Sie dies jedoch in der Eingabeaufforderung wie dieser tun, wird es immer nur so schnell wie nötig aktualisiert, indem Sie lediglich die Eingabeaufforderung aktualisieren. Es ist einfach.

Sie können auch etwas Ähnliches tun, wie Sie es tun, tailaber lieber festlegen

HISTFILE=${TGT_PTY}

Ich bearbeite es tatsächlich in einem anderen Tab ... Mehr Zeit bitte?
Mikeserv

Nun, ich drücke jetzt auf Speichern, @ illuminÉ - aber du wirst sehen, wo ich
aufgehört habe

@ illuminÉ - Übrigens - und ich hoffe, ich habe Recht damit - gehe ich davon aus, dass Sie den Befehl wie geschrieben eingegeben haben ${TGT_PTY}und alles? Wenn ja, würde dies die "mehrdeutige Umleitung" erklären, da dies eine leere Variable wäre. Sie benötigen eine Datei. /dev/pts/[num]aller Wahrscheinlichkeit nach -
mikeserv

Es funktioniert! Der Druckbildschirm hat geholfen! Ich wünschte, Ihr erster und einziger Codeblock wäre das, was Sie in den Druckbildschirm eingefügt haben, und ein klarer Verweis auf die Auswahl der Punkte, zu denen die Ausgabe gehen soll, und die Eingabe der Registerkarte in die Funktion - nichts anderes ist erforderlich. Die Verwendung einer Variablen zur Beschreibung von etwas, das ich manuell eingeben musste, um es schnell zu versuchen, ist meiner Meinung nach keine gute Praxis, da Sie sich in den Text vertiefen müssen, um alles herauszufinden, was die Lösung verschleiert. Auch alles Gute für Sie und Ihre Familie.

@ IlluminÉ - egal, catich war paranoid. Es funktioniert gut - sogar 12 Stunden später.
Mikeserv

4

Fühlen Sie sich frei, mit der Formatierung zu spielen, aber dies (glaube ich) macht das, wonach Sie fragen ... Speichern Sie es irgendwo in Ihrem Pfad, machen Sie es ausführbar und genießen Sie:

#!/bin/bash
count=$(  echo "scale=0 ; $(cat ~/.bash_history | wc -l ) / 2" | bc -l )
tail -f ~/.bash_history | awk -v c=$count '{if($1 ~/^#/){gsub(/#/, "", $1);printf "%s\t", c; "date \"+%F %T\" --date @" $1 | getline stamp; printf "[%s]\t",stamp;c++}else{print $0}}'

Ich bin sicher, dass es optimiert werden kann, aber Sie bekommen die Idee.

Kurze Erklärung: Da die ~ / .bash_history die Anzahl nicht verfolgt, bestimmen wir zuerst die Anzahl der Einträge. Dann ein bisschen Magie, um die Formatierung richtig zu machen und die Anzahl der Einträge zu verfolgen.


Das funktioniert nicht, wenn mehrzeilige Einträge vorhanden sind. Liest tail -fauch zunächst 10 Zeilen, die bereits in Ihrem enthalten sind count. Es wird das GNU-Datum in einer Nicht-POSIX-Umgebung angenommen (POSIXLY_CORRECT nicht festgelegt). Pro Zeitstempel werden eine Shell und ein Datumsbefehl ausgeführt.
Stéphane Chazelas

@StephaneChazelas Für die mehrzeiligen Einträge scheinen sie sich in meinem Setup bei beiden Lösungen zu registrieren. Etwas in meinem Setup vielleicht?

1
@ illuminÉ, tink's countzählt die halbe Zeile in .bash_historyund erhöht sich dann für jede Zeile, die nicht mit beginnt #, sodass die gemeldete Verlaufszahl wahrscheinlich falsch ist. Die Verwendung count=$(grep -c '^#' ...)wäre wahrscheinlich besser, aber in jedem Fall sind diese Verlaufsnummern wahrscheinlich nicht synchron, insbesondere wenn mehr als 2 Bash gleichzeitig ausgeführt werden.
Stéphane Chazelas

@StephaneChazelas Mit freundlichen Grüßen, da ich beide Lösungen nicht vollständig einschätzen kann, bin ich sehr dankbar für die Erklärung! In der Tat ist die Anzahl anders, folglich viel höher hier ... Ich kann sehen, dass Sie Ihre eigene Lösung um die Strftime herum erstellt haben, was im Grunde das ist, was der historyBefehl nutzt.

1
Vielen Dank für das Feedback @StephaneChazelas, ich werde sehen, ob ich diese
umgehen
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.