Eu tenho um script de instalação para uma caixa do Vagrant em que costumava medir etapas únicas time
. Agora eu gostaria de ativar ou desativar condicionalmente as medições de tempo.
Por exemplo, anteriormente uma linha seria semelhante a:
time (apt-get update > /tmp/last.log 2>&1)
Agora eu pensei que poderia simplesmente fazer algo assim:
MEASURE_TIME=true
[[ $MEASURE_TIME = true ]] && TIME="time --format=%e" || TIME=""
$TIME (apt-get update > /tmp/last.log 2>&1)
Mas isso não vai funcionar:
syntax error near unexpected token `apt-get'
`$TIME (apt-get update > /tmp/last.log 2>&1)'
Qual é o problema aqui?
Respostas:
Para poder cronometrar um subshell, você precisa da
time
palavra - chave , não do comando.A
time
palavra-chave, parte do idioma, é reconhecida apenas como tal quando inserida literalmente e como a primeira palavra de um comando (e, no caso deksh93
, o próximo token não começa com a-
). Mesmo a entrada"time"
não funcionará muito menos$TIME
(e seria tomada como uma chamada aotime
comando).Você pode usar aliases aqui que são expandidos antes que outra rodada de análise seja realizada (para que o shell reconheça essa
time
palavra-chave):A
time
palavra-chave não leva opções (exceto-p
embash
), mas o formato pode ser definido com aTIMEFORMAT
variávelbash
. (ashopt
parte também ébash
específica, outras conchas geralmente não precisam disso).fonte
info -f bash --index-search=time
Embora uma
alias
seja uma maneira de fazer isso, isso também pode ser feitoeval
- é que você não deseja tantoeval
a execução do comando quantoeval
a declaração do comando .Eu gosto de
alias
es - eu os uso o tempo todo, mas gosto mais de funções - especialmente sua capacidade de lidar com parâmetros e que eles não precisam necessariamente ser expandidos na posição de comando, conforme é necessário paraalias
es.Então eu pensei que talvez você queira tentar isso também:
A parte
$IFS
é principalmente sobre$*
. É importante que isso também( subshell bit )
seja o resultado de uma expansão do shell e, assim, para expandir os argumentos em uma string analisável que eu uso"$*"
(nãoeval
"$@"
use , a propósito, a menos que você tenha certeza de que todos os argumentos podem ser unidos em espaços) . O delimitador de divisão entre args in"$*"
é o primeiro byte in$IFS
e, portanto, pode ser perigoso continuar sem garantir seu valor. Assim, a função salva$IFS
, define-lo para um\n
ewline tempo suficiente paraset ... "$*"
nos"$3"
,unset
é ele, em seguida, redefine seu valor se ele já tinha um.Aqui está uma pequena demonstração:
Você vê que eu coloquei dois comandos no valor de
$TIME
lá - qualquer número é bom - mesmo nenhum -, mas tenha certeza de que foi escapado e citado corretamente - e o mesmo vale para os argumentos_time()
. Todos eles serão concatenados em uma única cadeia de comando quando forem executados - mas cada argumento recebe seu próprio\n
ewline e, portanto, pode ser espalhado com relativa facilidade. Ou então, você pode agrupá-los todos em um, se quiser, e separá-los em\n
linhas de telefone ou ponto-e-vírgula ou o que tiver. Apenas certifique-se de que um único argumento represente um comando que você se sinta confortável em colocar sua própria linha em um script quando o chamar.\(
, por exemplo, é bom, desde que seja seguido por\)
. Basicamente, as coisas normais.Quando
eval
o snippet acima é alimentado, ele se parece com:E seus resultados parecem ...
RESULTADO
O
hash
erro indica que não tenho um/usr/bin/time
instalado (porque não tenho) ecommand
vamos saber a que horas está sendo executado. O trailingset +x
é outro comando executado depoistime
(o que é possível) - é importante ter cuidado com os comandos de entrada ao executareval
qualquer coisa.fonte
_time() { eval "$TIME $@"; }
? Utilizando uma função de tem o inconveniente da introdução de um âmbito diferente para as variáveis (não é uma questão para subcamadas)"$@"
expansão. Eu gosto de um único argumento paraeval
- fica assustador de outra maneira. Ummm .... o que você quer dizer com o escopo diferente? Eu pensei que era apenasfunction
funções ...eval
junta seus argumentos antes de executar, e é isso que você está tentando fazer de uma maneira muito complicada. Eu quis dizer que_time 'local var=1; blah'
tornaria issovar
local para isso_time
, ou que_time 'echo "$#"'
imprimiria a função$#
dessa_time
função, não a do chamador.eval
concatena seus argumentos em espaços - que é, como você diz, exatamente o que faço aqui - embora não tenha sido uma intenção inicial. Eu vou consertar isso.eval
por outro lado"$*"
,"$@"
é um hábito que adquiri depois de muitoscommand not found
desentendimentos com quando args foram juntados no lugar errado. De qualquer forma, embora os argumentos sejam finalmente executados na função, é simples o suficiente expandi-los na invocação, eu acho. É o que eu faria de"$#"
qualquer maneira.