¿Cómo tener un historial de comandos con salida de marcas de tiempo en el terminal continuamente?

9

Utilizo un alias simple para habilitar el "seguimiento" de comandos en una o varias ventanas de terminal:

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

Luego solo tengo tail -fmi archivo .bash_history en otra terminal en el espacio de trabajo para obtener comentarios inmediatos. Acabo de habilitar el historial ilimitado y actualicé mi formato de historial ( export HISTTIMEFORMAT="[%F %T] ") en .bashrc . Por supuesto, el historycomando muestra las marcas de tiempo. Pero el formato del archivo de historial per se es:

#1401234303
alias
#1401234486
cat ../.bashrc 

¿Cómo puedo convertir el tiempo de Unix y mostrar todo el comando en una sola línea al igual que con el historycomando, incluida la numeración:

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

... y sigue eso. ¿O encuentra una manera de emitir continuamente la salida del historycomando al terminal?

Comunidad
fuente

Respuestas:

7

Con GNU awk:

tail -fn+1 ~/.bash_history | awk '
  /^#/{printf "%-4d [%s] ", ++n, strftime("%F %T", substr($0, 2)); next}; 1'
Stéphane Chazelas
fuente
Funciona muy bien e inicialmente se muestra más rápido si actualizo la otra solución fn+1para comparar. ¡Gracias!
5

Aquí está el producto final en acción en un xterm de pantalla dividida, desde los valores predeterminados de shell hasta el trabajo en solo un par de comandos:

ingrese la descripción de la imagen aquí

Una forma más dura de hacer esto que la que se muestra en la captura de pantalla podría verse así:

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

¿Dónde ${TGT_PTY}estaría lo que obtiene del ttycomando cuando realmente ejecuta un shell interactivo en la pantalla donde desea su salida? O, realmente, podría usar cualquier archivo que se pueda escribir, ya que esencialmente es solo un objetivo para la redirección de archivos.

Utilizo la sintaxis pty para pseudo-terminal porque supongo que es un xterm de algún tipo, pero podría dedicar un vt con la misma facilidad, y su historial transmitido siempre está a solo una CTRL-ALT-Fncombinación de teclas. Si fuera yo podría combinar las dos nociones y convertirla en una screeno tmuxsesión en un vt dedicada ... Pero estoy divagando.

En una máquina recién iniciada, recibo el típico /bin/loginaviso en una gettyconsola típica de Linux . Presiono CTRL-ALT-F2para acceder a una kmsconconsola menos típica que se comporta mucho más como un xtermque a tty. Entro en el comando ttyy recibo en respuesta /dev/pts/0.

En general, xterms multiplexa un dispositivo de terminal único en múltiples usando pseudo terminales , por lo que si hiciera algo similar en X11 al cambiar entre pestañas de terminal o ventanas, probablemente también recibiría la salida /dev/pts/[0-9]*. Pero las consolas de terminal virtual a las que se accede con CTRL-ALT-Fncombinaciones de teclas son dispositivos de terminal verdaderos (er) y, por lo tanto, reciben su propia /dev/tty[0-9]*designación.

Es por eso que después de iniciar sesión en la consola 2 cuando escribo ttyen el indicador, la respuesta es /dev/pts/0pero cuando hago lo mismo en la consola 1, la salida es /dev/tty1. En cualquier caso, de vuelta en la consola 2, entonces hago:

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

No hay efecto discernible. Continúo escribiendo algunos comandos más y luego cambio a la consola 1 presionando CTRL-ALT-F1nuevamente. Y allí encuentro entradas repetidas que se parecen <date_time>\n<hist#>\t<hist_cmd_string>a cada comando que escribí en la consola 2.

Sin embargo, salvo escribir directamente en un dispositivo terminal, otra opción podría ser algo como:

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

Y entonces tal vez ...

less +F ${TGT_PTY}

El comando de solicitud aproximada no cumple con sus especificaciones, sin cadenas de dateformato ni opciones de formato para fcninguna de ellas, pero su mecanismo no requiere mucho: cada vez que su solicitud representa el último comando del historial y la fecha y hora actuales se escriben en El ${TGT_PTY}archivo que especifique. Es tan simple como eso.

Mirar e imprimir el historial de shell es fcel propósito principal. Es un shell incorporado, incluso si dateno lo es. En zsh fcpuede proporcionar todo tipo de opciones de formato sofisticadas, varias de las cuales se aplican a las marcas de tiempo. Y, por supuesto, como observas más arriba, bashlos s historypueden hacer lo mismo.

En aras de una salida más limpia, puede usar una técnica que he explicado mejor aquí para establecer una variable de seguimiento persistente en el shell actual a pesar de tener que rastrearlo y procesarlo en subcapas dentro de la secuencia de solicitud.

Aquí hay un medio portátil de formatear según sus especificaciones:

_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 el contador last_history$_LH que solo rastrea las últimas actualizaciones para que no escriba el mismo comando de historial dos veces, por ejemplo, solo por presionar enter. Hay un poco de discusión necesaria para que la variable se incremente en el shell actual para que conserve su valor a pesar de que la función se llama en un subshell, que, una vez más, se explica mejor en el enlace .

Su salida se ve como <hist#>\t[%F %T]\t<hist_cmd>\n

Pero esa es solo la versión totalmente portátil. Con bashesto se puede hacer con menos y mediante la implementación solo de los componentes integrados de shell, lo que probablemente sea deseable cuando se considera que este es un comando que se ejecutará cada vez que presione [ENTER]. Aquí hay dos formas:

_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

Alternativamente, utilizando bashel historycomando 's , puede definir la _HISTfunción de esta manera:

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

La salida para cualquiera de los métodos también se ve así: <hist#>\t[%F %T]\t<hist_cmd>\naunque el historymétodo incluye algunos espacios en blanco iniciales. Aún así, creo que las historymarcas de tiempo del método serán más precisas ya que no creo que tengan que esperar a que se complete el comando referenciado antes de adquirir su sello.

Puede evitar el seguimiento de cualquier estado en ambos casos si solo de alguna manera filtra la transmisión uniq, como podría hacer con mkfifolo que mencioné antes.

Pero hacerlo en el indicador de esta manera significa que siempre se actualiza solo tan pronto como sea necesario por la simple acción de actualizar el indicador. Es simple.

También puede hacer algo similar a lo que está haciendo, tailpero más bien establecer

HISTFILE=${TGT_PTY}
mikeserv
fuente
En realidad lo estoy editando en otra pestaña ... ¿Más tiempo por favor?
mikeserv
Bueno, voy a golpear ahorrará ahora realidad, @ Illuminé - pero verá donde lo dejé ...
mikeserv
@ illuminÉ - Por cierto - y espero tener razón sobre esto - ¿Asumo que ingresaste el comando como está escrito ${TGT_PTY}y todo? Si es así, explicaría la 'redirección ambigua' porque sería una variable vacía. Necesitas un archivo. /dev/pts/[num]con toda probabilidad -
mikeserv
Funciona! ¡La pantalla de impresión ayudó! Desearía que tu primer y único bloque de código fuera lo que pusiste en la pantalla de impresión y una referencia clara para elegir a qué puntos quieres que vaya la salida e ingresar la pestaña en la función; no se requiere nada más. Usar una variable para describir algo que tuve que ingresar manualmente para probarlo rápidamente no es una buena práctica en mi opinión, ya que tienes que profundizar en el texto para resolverlo todo, lo que oculta la solución. También todo lo mejor para ti y tu familia.
@ illuminÉ: no importa catsi estaba paranoico. Funciona bien, incluso 12 horas después.
mikeserv
4

Siéntase libre de jugar con el formato, pero esto (creo) hace lo que está pidiendo ... guardar en algún lugar de su RUTA, hacer ejecutable y disfrutar:

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

Estoy seguro de que se puede optimizar, pero se entiende la idea.

Breve explicación: como ~ / .bash_history no realiza un seguimiento del recuento, primero determinamos el número de entradas. Luego, un poco de magia awk para obtener el formato correcto y realizar un seguimiento de la cantidad de entradas.

tink
fuente
Eso no funciona si hay entradas de varias líneas. También tail -fleerá 10 líneas inicialmente que ya se han incluido en su count. Asume una fecha GNU en un entorno que no es POSIX (POSIXLY_CORRECT no establecido). Ejecuta un comando de shell y un comando de fecha por marca de tiempo.
Stéphane Chazelas
@StephaneChazelas Para las entradas de varias líneas, en mi configuración parecen registrarse con ambas soluciones. ¿Algo en mi configuración tal vez?
1
@ illuminÉ, tink's countcuenta la mitad de la línea .bash_historyy luego se incrementa para cada línea que no comienza #, por lo que es probable que el conteo del historial informado sea incorrecto. El uso count=$(grep -c '^#' ...)probablemente sería mejor, pero en cualquier caso, es probable que esos números del historial terminen desincronizados, especialmente si tiene más de 2 bash ejecutándose al mismo tiempo.
Stéphane Chazelas
@StephaneChazelas Sinceramente, ya que no puedo apreciar completamente ninguna de las soluciones, ¡estoy muy agradecido por la explicación! De hecho, el recuento es diferente, mucho más alto, en consecuencia aquí ... Puedo ver que construyó su propia solución alrededor del tiempo de espera, que es básicamente lo que historyaprovecha el comando.
1
Gracias por la retroalimentación @StephaneChazelas, veré si puedo
evitarlos