Como eu posso condicionalmente passar um subshell através de 'time'?

9

Eu tenho um script de configuração para uma caixa do Vagrant, onde eu costumava medir etapas simples com time . Agora, gostaria de ativar ou desativar condicionalmente as medições de tempo.

Por exemplo, anteriormente uma linha seria parecida com:

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 funcionará:

syntax error near unexpected token 'apt-get'
'$TIME (apt-get update > /tmp/last.log 2>&1)'

Qual é o problema aqui?

    
por Der Hochstapler 17.12.2014 / 13:24

2 respostas

13

Para poder tempo um subshell, você precisa da palavra-chave time , não do comando.

A palavra-chave time , parte do idioma, só é reconhecida como tal quando inserida literalmente e como a primeira palavra de um comando (e, no caso de ksh93 , o próximo token não começa com um %código%). Mesmo inserir - não funcionará, muito menos "time" (e será considerado como uma chamada para o comando $TIME ).

Você poderia usar aliases aqui que são expandidos antes que outra rodada de análise seja realizada (assim, permitiria que o shell reconheça que time keyword):

shopt -s expand_aliases
alias time_or_not=
TIMEFORMAT=%E

MEASURE_TIME=true
[[ $MEASURE_TIME = true ]] && alias time_or_not=time

time_or_not (apt-get update > /tmp/last.log 2>&1)

A palavra-chave time não aceita opções (exceto time in -p ), mas o formato pode ser definido com a variável bash em TIMEFORMAT . (a parte bash também é shopt -específica, outras camadas geralmente não precisam disso).

    
por 17.12.2014 / 15:37
3

Enquanto um alias é uma maneira de fazer isso, isso pode ser feito com eval também - é só que você não quer muito eval a execução do comando como você deseja eval o comando declaração .

Eu gosto de alias es - eu os uso o tempo todo, mas gosto mais de funções - especialmente de sua capacidade de manipular parâmetros e de que eles não precisam necessariamente ser expandidos na posição de comando, como é necessário para alias es .

Então, achei que você gostaria de tentar também:

_time() if   set -- "${IFS+IFS=\;}" "$IFS" "$@" && IFS='
';      then set -- "$1" "$2" "$*"; unset IFS
             eval "$1 $TIME ${3#"$1"?"$2"?}"
        fi

O $IFS bit é principalmente sobre $* . É importante que o ( subshell bit ) seja também o resultado de uma expansão do shell e, assim, para expandir os argumentos em uma string analisável eu uso "$*" (não eval "$@" , a propósito, a menos que você esteja certo de que todos os args podem ser unidos em espaços) . O delimitador de divisão entre args em "$*" é o primeiro byte em $IFS e, portanto, pode ser perigoso continuar sem garantir seu valor. Assim, a função salva $IFS , configura para \n ewline tempo suficiente para set ... "$*" em "$3" , unset s e redefine seu valor se tiver um anteriormente.

Aqui está uma pequena demonstração:

TIME='set -x; time'
_time \( 'echo "$(echo any number of subshells)"' \
         'command -V time'                        \
         'hash time'                              \
      \) 'set +x'

Você vê que eu coloquei dois comandos no valor de $TIME there - qualquer número é bom - mesmo nenhum - mas tenha certeza de que é escapado e citado corretamente - e o mesmo vale para os argumentos para _time() . Eles serão todos concatenados em uma única cadeia de comandos quando forem executados - mas cada argumento obtém seu próprio \n ewline e, portanto, eles podem ser distribuídos com relativa facilidade. Ou então você pode juntá-los todos em um, se quiser, e separá-los em \n ewlines ou ponto e vírgula ou what-have-you. Apenas certifique-se de que um único argumento represente um comando que você se sinta confortável colocando em sua própria linha em um script quando o chamar. \( , por exemplo, é bom, desde que seja seguido por \) . Basicamente o material normal.

Quando eval recebe o snippet acima, parece que:

+ eval 'IFS=$2;set -x; time (
echo "$(echo any number of subshells)"
command -V time
hash time
)
set +x'

E seus resultados parecem ...

OUTPUT

+++ echo any number of subshells
++ echo 'any number of subshells'
any number of subshells
++ command -V time
time is a shell keyword
++ hash time
bash: hash: time: not found

real    0m0.003s
user    0m0.000s
sys     0m0.000s
++ set +x

O erro hash indica que não tenho um /usr/bin/time instalado (porque não o tenho) e command nos informa qual tempo está sendo executado. O trailing set +x é outro comando executado após time (o que é possível) - é importante ter cuidado com comandos de entrada quando eval ing qualquer coisa. / p>     

por 18.12.2014 / 11:13