Gravar saída de 'time' em um arquivo, por que os parênteses são necessários?

17

time grava em stderr , portanto, seria de se supor que adicionar 2>&1 à linha de comando deveria rotear sua saída para stdout . Mas isso não funciona:

test@debian:~$ cat file 
one two three four
test@debian:~$ time wc file > wc.out 2>&1

real    0m0.022s
user    0m0.000s
sys     0m0.000s
test@debian:~$ cat wc.out 
 1  4 19 file

Apenas com parênteses, funciona:

test@debian:~$ (time wc file) > wc.out 2>&1
test@debian:~$ cat wc.out 
 1  4 19 file

real    0m0.005s
user    0m0.000s
sys     0m0.000s

Por que os parênteses são necessários neste caso? Por que não é time wc interpretado como um único comando ?

    
por wolf-revo-cats 19.01.2016 / 14:13

5 respostas

23

Em ksh , bash e zsh , time não é um comando (incorporado ou não), é uma palavra reservada na linguagem como for ou while .

Está acostumado a cronometrar um pipeline 1 .

Em:

time for i in 1 2; do cmd1 "$i"; done | cmd2 > redir

Você tem uma sintaxe especial que diz ao shell para executar essa linha de tubulação:

for i in 1 2; do cmd1 "$i"; done | cmd2 > redir

E informe estatísticas de tempo para isso.

Em:

time cmd > output 2> error

É o mesmo, você está timing no comando cmd > output 2> error , e as estatísticas de tempo ainda vão no stderr do shell.

Você precisa:

{ time cmd > output 2> error; } 2> timing-output

Ou:

exec 3>&2 2> timing-output
time cmd > output 2> error 3>&-
exec 2>&3 3>&-

Para o stderr do shell ser redirecionado para timing-output antes da construção da hora (novamente, não comando ) é usado (aqui para a hora cmd > output 2> error 3>&- ).

Você também pode executar a construção time em um subshell que tenha seu stderr redirecionado:

(time cmd > output 2> error) 2> timing-output

Mas essa subshell não é necessária aqui, você só precisa que o stderr seja redirecionado no momento em que time construct é invocado.

A maioria dos sistemas também possui um comando time . Você pode invocar essa desativando a palavra-chave time . Tudo o que você precisa fazer é citar essa palavra-chave de alguma forma, pois as palavras-chave só são reconhecidas como tal quando são literais.

'time' cmd > output 2> error-and-timing-output

Mas cuidado, o formato pode ser diferente e o stderr de time e cmd será mesclado em error-and-timing-output .

Além disso, o comando time , em oposição à construção time , não pode deduzir tempo em pipelines ou compor comandos ou funções ou recursos internos do shell ...

Se fosse um comando interno, poderia ser capaz de invocar invocações de tempo ou funções internas, mas não poderia redirecionar redirecionamentos, pipelines ou comandos compostos.

1 Note que bash tem (o que pode ser considerado como um bug pelo qual time (cmd) 2> file (mas não time cmd | (cmd2) 2> file , por exemplo) redireciona a saída de tempo para file

    
por 19.01.2016 / 15:36
7

Não há nenhum comando chamado time wc , time e wc separado em palavra no shell.

Agora, muitas vezes há dois programa chamados time , o que é shell palavra-chave, outra é o comando externo . Em shells que time é uma palavra-chave do shell, quando você digita time wc ... , o shell usou sua palavra-chave time em vez do externo time utilitário .

Quando o shell usa a palavra-chave time , ele não precisa fork () novo processo, o atual time padrão e o erro padrão não são alterados. A parte de redirecionamento em:

time wc file > wc.out 2>&1

afeta apenas wc .

Quando você usa o comando composto (list) :

(time wc file) > wc.out 2>&1

o shell executou time wc file dentro de um subshell, (time wc file) foi considerado um comando único e a parte de redirecionamento afeta sua saída padrão e erro padrão, que agora incluem time e wc .

Você pode ter o mesmo efeito, sem o custo de bifurcar novo processo usando outra forma de comando de agrupamento {list;} :

{time wc file;} > wc.out 2>&1

Se você usa time externo, não enfrenta esse problema porque foi executado em um novo processo:

/usr/bin/time wc file > wc.out 2>&1
    
por 19.01.2016 / 14:41
2

Porque time que você está executando é bash embutido. Bash processa isso de maneira especial.

Se você usar o% realtime binário, ele funcionará exatamente da maneira esperada:

/usr/bin/time wc file > wc.out 2>&1

Embora a saída desse tempo seja um pouco diferente:

 $ /usr/bin/time wc file > wc.out 
0.00user 0.00system 0:00.00elapsed ?%CPU (0avgtext+0avgdata1900maxresident)k
0inputs+8outputs (0major+82minor)pagefaults 0swaps
    
por 19.01.2016 / 14:37
1

Não é time que escreve as informações de hora. O time embutido faz com que o shell grave isso depois que o comando foi concluído. Mas o redirecionamento afeta apenas o comando.

No caso (time ...) , o redirecionamento é aplicado a toda a sub-rede.

    
por 19.01.2016 / 14:40
0

Como o tempo é um shell embutido, ele grava no stderr do shell , ao invés do stderr do comando.

O uso de parênteses força o comando inteiro em um shell filho cujo stderr pode ser redirecionado.

usando chaves produz um resultado similar sem realmente iniciar uma subshell

  { time who ; } > /tmp/timwho >& /tmp/xx 

(sim, você precisa do ponto-e-vírgula)

    
por 20.01.2016 / 22:10