Come fare in modo che la cronologia dei comandi con i timestamp venga inviata continuamente al terminale?


9

Uso un semplice alias per abilitare il "tracking" dei comandi in una o più finestre di terminale:

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

Quindi ho solo il tail -fmio file .bash_history in un altro terminale nell'area di lavoro per ottenere un feedback immediato. Ho appena abilitato la cronologia illimitata e aggiornato il mio formato cronologico ( export HISTTIMEFORMAT="[%F %T] ") in .bashrc . Ovviamente il historycomando visualizza i timestamp. Ma il formato del file cronologico in sé è:

#1401234303
alias
#1401234486
cat ../.bashrc 

Come posso avere il tempo Unix convertito e l'intero comando visualizzato su una sola riga proprio come con il historycomando, inclusa la numerazione:

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

... e seguilo. O trovare un modo per inviare continuamente l'output del historycomando al terminale?

Risposte:


7

Con GNU awk:

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

Funziona alla grande e inizialmente viene visualizzato più velocemente se aggiorno l'altra soluzione con fn+1per confrontare! Grazie!

5

Ecco il prodotto finale in azione su un xterm a schermo diviso dalle impostazioni predefinite della shell al funzionamento con solo un paio di comandi:

inserisci qui la descrizione dell'immagine

Un modo più semplice per farlo rispetto a quanto mostrato nello screenshot potrebbe apparire così:

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

Dove ${TGT_PTY}sarebbe qualsiasi cosa tu ottenga dal ttycomando quando effettivamente esegui una shell interattiva sullo schermo in cui vuoi il tuo output. O, davvero, potresti usare qualsiasi file scrivibile in quanto è essenzialmente solo una destinazione per il reindirizzamento dei file.

Uso la sintassi pty per lo pseudo-terminale perché suppongo che sia un xterm di qualche tipo, ma potresti dedicare facilmente un vt - e la tua cronologia in streaming è sempre solo una CTRL-ALT-Fncombinazione chiave. Se fossi in me potrei combinare le due nozioni e renderle una screeno tmuxsessione su un vt dedicato ... Ma sto divagando.

Su una macchina appena avviata sono accolto con il tipico /bin/loginprompt su una tipica gettyconsole Linux . Premo CTRL-ALT-F2per accedere a una kmsconconsole meno tipica che si comporta in modo molto più simile a una xtermche a tty. Inserisco il comando ttye ricevo in risposta /dev/pts/0.

Generalmente xterm multiplexa un singolo dispositivo terminale in multiplo usando pseudo-terminali - quindi se dovessi fare una cosa simile in X11 passando da una scheda terminale a un'altra finestra, probabilmente riceveresti anche l'output /dev/pts/[0-9]*. Ma le console terminali virtuali a cui si accede con CTRL-ALT-Fncombinazioni di tasti sono veri e propri dispositivi terminali e quindi ricevono la loro /dev/tty[0-9]*designazione.

Questo è il motivo per cui dopo aver effettuato l'accesso alla console 2 quando digito ttyal prompt la risposta è /dev/pts/0ma quando faccio lo stesso sulla console 1 l'output è /dev/tty1. In ogni caso, tornando sulla console 2, faccio quindi:

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

Non c'è alcun effetto riconoscibile. Continuo a digitare qualche altro comando e poi passo alla console 1 premendo di CTRL-ALT-F1nuovo. E lì trovo voci ripetute che assomigliano <date_time>\n<hist#>\t<hist_cmd_string>a ogni comando che ho digitato sulla console 2.

Escludendo la scrittura diretta su un dispositivo terminale, tuttavia, un'altra opzione potrebbe essere simile a:

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

E poi forse ...

less +F ${TGT_PTY}

Il comando del prompt approssimativo non soddisfa le tue specifiche - nessuna stringa di formato per datee nessuna opzione di formattazione per fcentrambi - ma il suo meccanismo non richiede molto: ogni volta che il tuo prompt esegue il rendering dell'ultimo comando della cronologia e la data e l'ora correnti vengono scritte in il ${TGT_PTY}file specificato. E 'così semplice.

Guardare e stampare la cronologia delle shell è fclo scopo principale. È una shell integrata, anche se datenon lo è. In zsh fcpuò fornire tutti i tipi di opzioni di formattazione fantasiose, molte delle quali si applicano ai timestamp. E naturalmente, come si nota in precedenza, bash's historypuò fare lo stesso.

Nell'interesse di un output più pulito, è possibile utilizzare una tecnica che ho spiegato meglio qui per impostare una variabile di tracciamento persistente nella shell corrente nonostante sia necessario rintracciarla ed elaborarla in subshells all'interno della sequenza di prompt.

Ecco un mezzo portatile per formattare secondo le tue specifiche:

_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

Implemento il contatore last_history$_LH che tiene traccia degli ultimi aggiornamenti in modo da non scrivere due volte lo stesso comando di cronologia, ad esempio semplicemente premendo Invio. È necessario un po 'di wrangling per ottenere l'incremento della variabile nella shell corrente in modo che mantenga il suo valore anche se la funzione è chiamata in una subshell - che è, ancora una volta, meglio spiegata nel collegamento .

Sembra che il suo output <hist#>\t[%F %T]\t<hist_cmd>\n

Ma questa è solo la versione completamente portatile. Con bashesso può essere fatto con meno e implementando solo i builtin della shell - il che è probabilmente auspicabile se si considera che questo è un comando che verrà eseguito ogni volta che si preme [ENTER]. Ecco due modi:

_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

In alternativa, usando bashil historycomando ', puoi definire la _HISTfunzione in questo modo:

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

Anche l'output per entrambi i metodi è simile: <hist#>\t[%F %T]\t<hist_cmd>\nsebbene il historymetodo includa alcuni spazi bianchi iniziali. Tuttavia, credo che i historytimestamp del metodo saranno più precisi in quanto non penso che dovrebbero attendere il completamento del comando di riferimento prima di acquisire il loro timbro.

Puoi evitare di tracciare qualsiasi stato in entrambi i casi se solo in qualche modo filtri lo stream con uniq, come potresti fare con mkfifocome ho già detto prima.

Ma farlo nel prompt in questo modo significa che è sempre aggiornato solo non appena è necessario semplicemente aggiornando il prompt. È semplice.

Potresti anche fare qualcosa di simile a quello che stai facendo tailma piuttosto impostare

HISTFILE=${TGT_PTY}

In realtà lo sto modificando in un'altra scheda ... Più tempo, per favore?
Mikeserv,

Bene, in questo momento colpirò il salvataggio, @ illuminÉ - ma vedrai da dove avevo interrotto ...
mikeserv,

@ illuminÉ - A proposito - e spero di avere ragione su questo - suppongo che tu abbia inserito il comando come scritto ${TGT_PTY}e tutto il resto? In tal caso, spiegherebbe il "reindirizzamento ambiguo" perché sarebbe una variabile vuota. Hai bisogno di un file. /dev/pts/[num]con ogni probabilità -
mikeserv,

Funziona! Lo schermo di stampa ha aiutato! Vorrei che il tuo primo e unico blocco di codice fosse quello che hai messo nella schermata di stampa e un chiaro riferimento alla scelta a quali punti vuoi che vada l'output e inserendo la scheda nella funzione - nient'altro è richiesto. L'uso di una variabile per descrivere qualcosa che ho dovuto inserire manualmente per provarlo rapidamente non è una buona pratica a mio avviso in quanto devi scavare nel testo per capire tutto, il che offusca la soluzione. Tutto il meglio per te e la tua famiglia.

@ illuminÉ - non importava che catfossi paranoico. Funziona bene, anche 12 ore dopo.
Mikeserv,

4

Sentiti libero di giocare con la formattazione, ma questo (credo) fa quello che stai chiedendo ... salva in qualche parte del tuo PERCORSO, rendi eseguibile e divertiti:

#!/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}}'

Sono sicuro che può essere ottimizzato, ma hai capito.

Breve spiegazione: poiché ~ / .bash_history non tiene traccia del conteggio, determiniamo innanzitutto il numero di voci. Quindi un po 'di awk magic per ottenere la formattazione corretta e tenere traccia del numero di voci.


Ciò non funziona se sono presenti voci su più righe. Inoltre tail -fleggerà inizialmente 10 righe che sono già state incluse nel tuo count. Presuppone la data GNU in un ambiente non POSIX (POSIXLY_CORRECT non impostato). Esegue una shell e un comando di data per timestamp.
Stéphane Chazelas,

@StephaneChazelas Per le voci multilinea, sulla mia configurazione sembrano registrarsi con entrambe le soluzioni. Qualcosa nel mio setup forse?

1
@ illuminÉ, i countconteggi di tink rappresentano metà della riga .bash_historye quindi aumentano per ogni riga che non inizia con #, quindi è probabile che il conteggio della cronologia riportato sia errato. L'uso count=$(grep -c '^#' ...)sarebbe probabilmente migliore, ma in ogni caso, è probabile che quei numeri storici finiscano fuori sincrono, specialmente se hai più di 2 bash in esecuzione contemporaneamente.
Stéphane Chazelas,

@StephaneChazelas Sinceramente poiché non riesco ad apprezzare appieno entrambe le soluzioni, sono molto grato per la spiegazione! In effetti il ​​conteggio è diverso, molto più elevato di conseguenza qui ... Vedo che hai costruito la tua soluzione intorno allo strftime, che è fondamentalmente ciò che il historycomando sfrutta.

1
Grazie per il feed-back @StephaneChazelas, vedrò se riesco a aggirare quei :)
tink
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.