Como ter o histórico de comandos com registros de data e hora no terminal continuamente?

9

Eu uso um alias simples para ativar o "rastreamento" de comandos em uma ou mais janelas do terminal:

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

Então, bastai otail -f meu arquivo .bash_history em outro terminal da área de trabalho para obter feedback imediato. Acabei de ativar o histórico ilimitado e atualizei o formato do meu histórico ( export HISTTIMEFORMAT="[%F %T] ") em .bashrc . Obviamente, o historycomando exibe os carimbos de data e hora. Mas o formato do arquivo de histórico em si é:

#1401234303
alias
#1401234486
cat ../.bashrc 

Como posso converter o horário do Unix e o comando inteiro exibido em uma única linha, como no historycomando, incluindo numeração:

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

... e siga isso. Ou encontrar uma maneira de produzir continuamente a saída do historycomando para o terminal?

Comunidade
fonte

Respostas:

7

Com o GNU awk:

tail -fn+1 ~/.bash_history | awk '
  /^#/{printf "%-4d [%s] ", ++n, strftime("%F %T", substr($0, 2)); next}; 1'
Stéphane Chazelas
fonte
Funciona muito bem e inicialmente é exibido mais rápido se eu atualizar a outra solução fn+1para comparar! Obrigado!
5

Aqui está o produto final em ação em um xterm de tela dividida, dos padrões basicamente do shell ao trabalho em apenas alguns comandos:

insira a descrição da imagem aqui

Uma maneira mais difícil de fazer isso do que é demonstrado na captura de tela pode ser assim:

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

Onde ${TGT_PTY}estaria o que você obtém do ttycomando ao executar um shell interativo na tela em que deseja sua saída. Ou, realmente, você pode usar qualquer arquivo gravável, pois é essencialmente apenas um alvo para o redirecionamento de arquivos.

Eu uso a sintaxe pty para pseudo-terminal, porque estou assumindo que é um xterm de algum tipo, mas você pode dedicar um vt com a mesma facilidade - e seu histórico transmitido é sempre apenas uma CTRL-ALT-Fncombinação de teclas. Se fosse comigo eu poderia combinar as duas noções e torná-lo um screenou tmuxsessão em um vt dedicado ... Mas eu discordo.

Em uma máquina recém-inicializada, sou recebido com o /bin/loginprompt típico em um gettyconsole Linux típico . Pressiono CTRL-ALT-F2para acessar um kmsconconsole menos típico que se comporta muito mais como um xtermque com um tty. Entro no comando ttye recebo em resposta /dev/pts/0.

Geralmente, os xterms multiplexam um único dispositivo de terminal em vários usando pseudo-terminais - portanto, se você fizesse algo semelhante no X11 alternando entre as guias ou janelas do terminal, provavelmente também receberia saída /dev/pts/[0-9]*. Mas os consoles de terminais virtuais acessados ​​com CTRL-ALT-Fncombinações de teclas são verdadeiros dispositivos de terminal e, portanto, recebem sua própria /dev/tty[0-9]*designação.

É por isso que, depois de entrar no console 2, quando digito ttyno prompt, a resposta é, /dev/pts/0mas quando faço o mesmo no console 1, a saída é /dev/tty1. De qualquer forma, de volta ao console 2, faço:

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

Não há efeito discernível. Continuo digitando mais alguns comandos e depois mudo para o console 1 pressionando CTRL-ALT-F1novamente. E lá encontro entradas repetidas que se parecem com <date_time>\n<hist#>\t<hist_cmd_string>todos os comandos que digitei no console 2.

Com exceção da gravação direta em um dispositivo terminal, outra opção poderia ser algo como:

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

E então talvez ...

less +F ${TGT_PTY}

O comando rough prompt não atende às suas especificações - nenhuma string de dateformatação para e nenhuma opção de formatação para fcelas - mas seu mecanismo não exige muito: toda vez que seu prompt renderiza o último comando do histórico e a data e hora atuais são gravadas em o ${TGT_PTY}arquivo que você especificar. É simples assim.

Observar e imprimir o histórico do shell é fco objetivo principal do. É um shell embutido, mesmo que datenão seja. O In zsh fcpode fornecer todos os tipos de opções de formatação sofisticadas, várias das quais se aplicam a carimbos de data e hora. E é claro, como você notou acima, basho historypode fazer o mesmo.

No interesse de uma saída mais limpa, você pode usar uma técnica que expliquei melhor aqui para definir uma variável de rastreamento persistente no shell atual, apesar de ter que rastreá-lo e processá-lo em subshells na sequência de prompt.

Aqui está um meio portátil de formatar com suas especificações:

_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

Eu implemento o contador last_history$_LH que apenas rastreia as atualizações mais recentes para que você não escreva o mesmo comando de histórico duas vezes - por exemplo, apenas pressionando enter. Há um pouco de disputa necessária para incrementar a variável no shell atual, para que ela retenha seu valor mesmo que a função seja chamada em um subshell - o que é novamente explicado melhor no link .

Sua saída parece <hist#>\t[%F %T]\t<hist_cmd>\n

Mas essa é apenas a versão totalmente portátil. Com bashisso, pode ser feito com menos e implementando apenas os recursos internos do shell - o que provavelmente é desejável quando você considera que este é um comando que será executado toda vez que você pressionar [ENTER]. Aqui estão duas maneiras:

_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

Como alternativa, usando basho historycomando 's , você pode definir a _HISTfunção desta maneira:

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

A saída para qualquer método também se parece com: <hist#>\t[%F %T]\t<hist_cmd>\nembora o historymétodo inclua alguns espaços em branco à esquerda. Ainda assim, acredito que os historycarimbos de data e hora do método serão mais precisos, pois não acho que precisariam esperar o comando referenciado ser concluído antes de adquirir o carimbo.

É possível evitar o rastreamento de qualquer estado nos dois casos, se você filtrar o fluxo de alguma forma uniq- como você pode fazer mkfifocomo mencionei antes.

Mas fazê-lo no prompt como este significa que ele sempre é atualizado apenas assim que necessário, apenas pela simples ação de atualizar o prompt. É simples.

Você também pode fazer algo semelhante ao que está fazendo, tailmas definir

HISTFILE=${TGT_PTY}
mikeserv
fonte
Na verdade, estou editando em outra guia ... Mais tempo, por favor?
Mikeerv
Bem, na verdade, eu vou clicar em salvar agora, @ illuminÉ - mas você verá de onde eu
parei
@ illuminÉ - A propósito - e espero estar certo sobre isso - suponho que você tenha digitado o comando como está escrito ${TGT_PTY}e tudo mais? Nesse caso, explicaria o 'redirecionamento ambíguo' porque essa seria uma variável vazia. Você precisa de um arquivo. /dev/pts/[num]com toda a probabilidade - #
mikeserv
Isso funciona! A tela de impressão ajudou! Desejo que o seu primeiro e único bloco de código seja o que você coloca na tela de impressão e uma referência clara para escolher quais pontos você deseja que a saída vá e inserir a guia na função - nada mais é necessário. Usar uma variável para descrever algo que eu precisei inserir manualmente para testá-lo rapidamente não é uma boa prática, na minha opinião, pois você precisa se aprofundar no texto para descobrir tudo, o que ofusca a solução. Também tudo de bom para você e sua família.
@ illuminÉ - deixa pra lá, cateu era paranóico. Funciona bem - até 12 horas depois.
mikeserv
4

Sinta-se livre para jogar com a formatação, mas isso (acredito) faz o que você está pedindo ... salve em algum lugar do seu PATH, torne o executável e aproveite:

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

Tenho certeza de que pode ser otimizado, mas você entendeu.

Breve explicação: como o ~ / .bash_history não acompanha a contagem, primeiro determinamos o número de entradas. Em seguida, um pouco de magia awk para obter a formatação correta e acompanhar o número de entradas.

consertar
fonte
Isso não funciona se houver entradas de várias linhas. Também tail -flerá 10 linhas inicialmente que já foram incluídas no seu count. Ele assume a data GNU em um ambiente não POSIX (POSIXLY_CORRECT não configurado). Ele executa um shell e um comando de data por carimbo de data / hora.
Stéphane Chazelas
@StephaneChazelas Para as entradas de várias linhas, na minha configuração, elas parecem se registrar nas duas soluções. Algo na minha configuração, talvez?
1
@ illuminÉ, o tink countconta metade da linha .bash_historye, em seguida, é incrementado para cada linha que não inicia #, portanto a contagem do histórico relatado provavelmente está incorreta. O uso count=$(grep -c '^#' ...)provavelmente seria melhor, mas, em qualquer caso, é provável que esses números do histórico acabem fora de sincronia, especialmente se você tiver mais de 2 bash em execução ao mesmo tempo.
Stéphane Chazelas
@StephaneChazelas Atenciosamente, como não posso apreciar totalmente nenhuma das soluções, estou muito agradecido pela explicação! De fato, a contagem é diferente, consequentemente muito mais alta aqui ... Eu posso ver que você construiu sua própria solução em torno do tempo do strft, que é basicamente o que o historycomando utiliza.
1
Obrigado pelas @StephaneChazelas feed-back, eu vou ver se eu posso trabalhar em torno daqueles :)
tink