Forçando um alias 'adicionado' a todos os comandos

11

É possível adicionar à força um alias de tempo (por falta de uma maneira melhor de expressá-lo) a cada comando bash?

Por exemplo, eu gostaria de ter um usuário específico que, sempre que um comando é executado, ele sempre é agrupado com dateantes e depois ou time.

Isso é possível e, em caso afirmativo, como?

Warren
fonte
Eu olhei antes e não consegui encontrar uma maneira de fazer exatamente isso. Como Caleb diz, você pode usar preexec, mas não deseja executá-lo dentro do preexec(por exemplo preexec() { time $1; }), porque o shell ainda o executa após o preexecretorno. Portanto, o melhor que podemos fazer é algo semelhante.
26611 Mikel
@ Mikel Eu acho que você poderia usar a preexec()função para realmente encapsular o que estava executando, buscando o comando, executando-o de dentro da função e retornando algum tipo de erro para que o shell não execute o próprio comando.
Caleb

Respostas:

10

Você pode registrar a hora em que uma linha de comando é iniciada e a hora em que um prompt é exibido. O Bash já acompanha a data de início de cada linha de comando em seu histórico e você pode observar a hora em que exibe o próximo prompt.

print_command_wall_clock_time () {
  echo Wall clock time: \
    $(($(date +%s) - $(HISTTIMEFORMAT="%s ";
                       set -o noglob;
                       set $(history 1); echo $2)))
}
PROMPT_COMMAND=print_command_wall_clock_time$'\n'"$PROMPT_COMMAND"

Isso fornece apenas uma segunda resolução e apenas o tempo do relógio de parede. Se você quiser uma melhor resolução, precisará usar um datecomando externo que suporte o %Nformato para nanossegundos e o DEBUGtrap para chamar dateantes de executar o comando no tempo.

call_date_before_command () {
  date_before=$(date +%s.%N)
}
print_wall_clock_time () {
  echo Wall clock time: \
    $((date +"$date_before - %s.%N" | bc))
}
trap call_date_before_command DEBUG
PROMPT_COMMAND=print_command_wall_clock_time

Mesmo com a DEBUGarmadilha, não acho que exista uma maneira de exibir automaticamente os tempos do processador para cada comando ou de ser mais discriminador do que o prompt a prompt.


Se você estiver disposto a usar um shell diferente, veja como obter um relatório de horário para cada comando no zsh (isso não generaliza para outras tarefas):

REPORTTIME=0

Você pode definir REPORTTIMEqualquer valor inteiro; as informações de tempo serão exibidas apenas para comandos que usaram mais do que muitos segundos de tempo do processador.

O Zsh pegou esse recurso do csh, onde a variável é chamada time.

Gilles 'SO- parar de ser mau'
fonte
3

Suas opções aqui vão depender do seu shell. Em zshhá uma função de gancho conveniente chamada preexec()que é executado logo antes de quaisquer comandos shell interativo. Ao criar uma função com esse nome, você pode fazer com que as coisas sejam executadas. Você também pode acompanhar uma função chamada precmd()que será executada pouco antes do próximo prompt ser desenhado, que será logo após a conclusão do comando.

Ao criar esse par de funções, você pode ter os comandos arbitrários que deseja executar antes e depois dos comandos emitidos no prompt. Você pode usar isso para registrar o uso do shell, criar bloqueios, testar o ambiente ou, como no seu exemplo, calcular o tempo ou os recursos gastos enquanto um comando é executado.

Neste exemplo, criaremos um carimbo de data / hora de referência antes de executar um comando preexec(), calcularemos o tempo gasto na execução do comando precmd()e o produziremos antes do prompt ou o registraremos. Exemplo:

preexec() {
   CMDSTART=$(date +%s%N)
}
precmd() {
   CMDRUNTIME=$(($(date +%s%N)-$CMDSTART))
   echo "Last command ran for $CMDRUNTIME nanoseconds."
}

Nota: Para este exemplo específico, há uma função interna ainda mais fácil. Tudo o que você precisa fazer é ativar os relatórios de tempo de execução no ZSH e isso será feito automaticamente.

$ export REPORTTIME=0
$ ls -d
./
ls -BF --color=auto -d  0.00s user 0.00s system 0% cpu 0.002 total

Em uma implementação mais prática preexec(), eu uso para ver se o shell está sendo executado dentro tmuxou screen, se for o caso, para enviar informações sobre o comando atualmente em execução upstream para serem exibidas no nome da guia.

Infelizmente, no bash, esse pequeno mecanismo não existe. Aqui está a tentativa de um homem de replicá-lo . Veja também a resposta de Gilles para um hack pequeno e bacana.

Caleb
fonte
1
E aqui está uma versão simplificada do truque precmd-DEBUG .
Gilles 'SO- stop be evil'
gostaria que isso estivesse disponível no bash!
Warren
Veja os links de Gilles e outros, é implementável no bash com um pouco de brincadeira extra. Novamente, por que você não executa o zsh? É uma concha de balanço com mais boas razões para mudar do que apenas isso!
Caleb
2
Se você estiver usando o zsh, há uma maneira ainda melhor de fazer isso. A variável de ambiente REPORTTIME, quando configurada, produzirá as informações do tempo de execução (como se você tivesse executado o comando com 'time' na frente) para qualquer comando que demorasse mais que $ REPORTTIME segundos. Basta configurá-lo como 0 e deve informar a hora de cada comando, com a divisão do usuário / sys para inicializar.
Joseph Garvin
1

A maneira mais fácil provavelmente seria definir PROMPT_COMMAND. Consulte Variáveis ​​Bash :

PROMPT_COMMAND
Se definido, o valor é interpretado como um comando a ser executado antes da impressão de cada prompt primário ( $PS1).

Por exemplo, para evitar a substituição de qualquer comando de prompt existente, você pode:

PROMPT_COMMAND="date ; ${PROMPT_COMMAND}"
cjm
fonte
não sabia sobre isso - parece um bom começo, @cjm
Warren
1
É um começo, mas não resolve o problema de saber quando um comando foi executado. O prompt em si pode ser desenhado minutos, horas ou dias antes de um comando ser digitado e executado.
Caleb
0

csh/ tcshtem o melhor suporte para esse recurso (e sempre o teve).

  The `time` shell variable can be set to execute the time builtin  command
  after the completion of any process that takes more than a given number
  of CPU seconds.

Em outras palavras, set time=1imprimirá o tempo consumido (sistema, usuário, decorrido) por qualquer comando que demorou mais de 1 segundo do tempo da CPU. Plain set timepermitirá a impressão do tempo para todos os comandos.

alexis
fonte