Prompt PS1 para mostrar o tempo decorrido

10

Atualmente, eu uso isso para exibir o horário atual no meu prompt do bash:

PS1=\[\e[0;32m\]\t \W>\[\e[1;37m\]

20:42:23 ~>

É possível exibir o tempo decorrido desde o prompt anterior? Tal como:

00:00:00 ~> sleep 10
00:00:10 ~> sleep 20
00:00:20 ~>

Isso não tem nada em comum. É possível alterar o PS1 periodicamente por um script em segundo plano?

TeasingDart
fonte
Não, não há resposta nessa postagem e espero que o prompt mude apenas quando um novo prompt for exibido.
TeasingDart
Não existe realmente uma maneira viável de fazer o que você pede, não.
DopeGhoti
1
É possível se o valor exibido for estático (a pergunta do OP não pareceu permitir isso). O tempo decorrido pode ser mantido salvando o tempo da época do tempo anterior em uma variável de shell. Implementar parece muito trabalhoso (uma hora mais ou menos - talvez alguém forneça uma solução mais simples do que eu tenho em mente). Esta pergunta seria útil.
Thomas Dickey

Respostas:

10

Uma maneira de fazer isso seria usar o recurso PROMPT_COMMAND do bash para executar o código que modifica o PS1. A função abaixo é uma versão atualizada do meu envio original; este usa duas variáveis ​​de ambiente a menos e as prefixa com "_PS1_" para tentar evitar as variáveis ​​existentes.

prompt_command() {
  _PS1_now=$(date +%s)
  PS1=$( printf "\[\e[0;32m\]%02d:%02d:%02d \W>\[\e[1;37m\] " \
           $((  ( _PS1_now - _PS1_lastcmd ) / 3600))         \
           $(( (( _PS1_now - _PS1_lastcmd ) % 3600) / 60 )) \
           $((  ( _PS1_now - _PS1_lastcmd ) % 60))           \
       )
  _PS1_lastcmd=$_PS1_now
}
PROMPT_COMMAND='prompt_command'
_PS1_lastcmd=$(date +%s)

Coloque isso no seu .bash_profile para iniciar as coisas.

Observe que você precisa digitar rapidamente para que o sleepparâmetro corresponda ao parâmetro prompt - o tempo é realmente a diferença entre os prompts, incluindo o tempo que você leva para digitar o comando.

00:00:02 ~> sleep 5   ## here I typed really quickly
00:00:05 ~> sleep 3   ## here I took about 2 seconds to enter the command
00:00:10 ~> sleep 30 ## more slow typing
00:01:35 ~>

Adição tardia:

Com base na resposta agora excluída do @Cyrus, aqui está uma versão que não sobrecarrega o ambiente com variáveis ​​extras:

PROMPT_COMMAND='
    _prompt(){
        PROMPT_COMMAND="${PROMPT_COMMAND%-*}-$SECONDS))\""
        printf -v PS1 "\[\e[0;32m\]%02d:%02d:%02d \W>\[\e[1;37m\] " \
                      "$(($1/3600))" "$((($1%3600)/60))" "$(($1%60))"
    }; _prompt "$((SECONDS'"-$SECONDS))\""

Adição extra tardia:

A partir da versão 4.2 ( echo $BASH_VERSION) do bash , é possível evitar as datechamadas externas com uma nova string de formato printf; substitua as $(date +%s)peças por $(printf '%(%s)T' -1). A partir da versão 4.3 , é possível omitir o -1parâmetro para confiar no comportamento "nenhum argumento significa agora ".

Jeff Schaller
fonte
Isso é bem próximo. Funciona quando copio / colo de um prompt do bash, mas quando tentei adicioná-lo ao meu .bashrc, ele imprime "1451424431: comando não encontrado"
TeasingDart
talvez um pouco demais tenha copiado / colado?
Jeff Schaller
Esta última versão funcionou, exatamente o que eu queria! Eu acho que tinha algo a ver com minha armadilha para definir a cor do texto após o prompt. Obrigado.
TeasingDart
quando você redefinir $SECONDSdeixa de controlar o tempo desde o shell começou,
mikeserv
1
@chepner - bem, claro, mas não mantém o tempo do shell novamente. não me interpretem mal - votei positivo porque é uma boa resposta - mas acho que a redefinição de um shell interativo $SECONDSpara cada prompt provavelmente provocará comportamentos inesperados. qualquer outra função shell que possa usá-la por qualquer motivo associado à avaliação do tempo de execução se comportará mal.
mikeserv
4
PS1[3]=$SECONDS
PS1='${PS1[!(PS1[1]=!1&(PS1[3]=(PS1[2]=$SECONDS-${PS1[3]})/3600))
   ]#${PS1[3]%%*??}0}$((PS1[3]=(PS1[2]/60%60),  ${PS1[3]})):${PS1[1
   ]#${PS1[3]%%*??}0}$((PS1[3]=(PS1[2]%60),     ${PS1[3]})):${PS1[1
   ]#${PS1[3]%%*??}0}$((PS1[3]=(SECONDS),       ${PS1[3]})):'$PS1

Isso lida com a formatação pelo cálculo - portanto, embora se expanda várias vezes, não faz subcascas ou tubos.

Ele apenas trata $PS1como uma matriz e usa os índices mais altos para armazenar / calcular todo / qualquer estado necessário entre os prompts. Nenhum outro estado do shell é afetado.

00:00:46:[mikeserv@desktop tmp]$
00:00:01:[mikeserv@desktop tmp]$
00:00:00:[mikeserv@desktop tmp]$
00:00:01:[mikeserv@desktop tmp]$
00:00:43:[mikeserv@desktop tmp]$ sleep 10
00:00:33:[mikeserv@desktop tmp]$ sleep 10
00:00:15:[mikeserv@desktop tmp]$
00:00:15:[mikeserv@desktop tmp]$
00:00:02:[mikeserv@desktop tmp]$
00:02:27:[mikeserv@desktop tmp]$

Eu posso dividir um pouco, talvez ...

Primeiro, salve o valor atual de $SECONDS:

PS1[3]=$SECONDS

Em seguida, defina $PS1[0]para ser auto-recursivo de uma maneira que sempre defina os valores corretos ao $PS1[1-3]mesmo tempo em que se auto-referencia. Para obter essa parte, você deve considerar a ordem em que as expressões shell-math são avaliadas. Mais importante, shell-math é sempre a última ordem de negócios para shell-math. Antes de tudo, o shell expande valores. Dessa maneira, você pode fazer referência a um valor antigo para uma variável de shell em uma expressão matemática depois de atribuí-la usando $.

Aqui está um exemplo simples primeiro:

x=10; echo "$(((x+=5)+$x+x))" "$x"

40 15

O shell avaliará essa instrução substituindo primeiro o valor de $xonde a $referência do cifrão é usada e, portanto, a expressão se torna:

(x+=5)+10+x

... então o shell adiciona 5 ao valor de $xe depois expande toda a expressão para x+10+x, mantendo apenas o valor realmente atribuído na variável de referência. Portanto, o valor expandido da expressão matemática é 40, mas o valor final de $xé 15.

É assim também que a $PS1equação funciona, exceto que há um nível adicional de expansão / avaliação matemática explorada nos índices da matriz.

PS1='${PS1[!(PS1[1]=!1&(...))]#...}...'

Não sei ao certo por que escolhi usar PS1[1]=!1lá - acho que provavelmente era apenas uma estética boba - mas isso atribui 0 a $PS1[1]enquanto o expande para substituição de parâmetros. O valor de um AND bit a bit para 0 e qualquer outra coisa sempre será 0, mas não funciona como um booleano &&quando o primário mais à esquerda é 0 e, portanto, a expressão entre parênteses ainda é avaliada todas as vezes. Isso é importante, é claro, porque nessa primeira elipse é onde $PS1[2,3]são definidos os valores iniciais .

De qualquer forma, $PS1[1]aqui é garantido que seja 0, mesmo se for violado entre os sorteios imediatos. Dentro dos parênteses lá ...

PS1[3]=(PS1[2]=$SECONDS-${PS1[3]})/3600

... $PS1[2]é atribuída a diferença de $PS1[3]e $SECONDS, e $PS1[3]é atribuído o quociente desse valor e 3600. Todos os valores são aqui inicializados. E entao:

${PS1[1]#${PS1[3]%%*??}0}

... se houver pelo menos dois dígitos $PS1[3], a expansão interna será nula e, como sabemos que $PS1[1]é 0, se ela $PS1[3]puder ser substituída por nada, também $PS1[1]será expandida para seu valor. Dessa maneira, somente os valores de um dígito para cada iteração de $PS1[3]atribuições expandirão um zero inicial, e $PS1[3]ele próprio será expandido no módulo 60 imediatamente depois, sendo simultaneamente atribuído o próximo valor sucessivamente menor para cada hora, minutos e segundos.

Enxágüe e repita, até a última iteração, quando $PS1[3]é substituída pelo valor atual de, $SECONDSpara que possa ser comparada $SECONDSmais uma vez quando o prompt for desenhado.

mikeserv
fonte
1

A melhor solução que encontrei até agora é esta: https://github.com/jichu4n/bash-command-timer

Que imprime [ 1s011 | May 25 15:33:44 BST ]aka o tempo decorrido no lado direito após o comando executado, para não incomodar o PS1.

O formato completo de sequência e hora é configurável. Até a cor e a precisão são configuráveis. Eu sei que pode ser um pouco demais para alguns minimalistas por aí, mas é bem legal.

sebs
fonte