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

18

timeescreve para stderr, então alguém poderia assumir que adicionar 2>&1à linha de comando deve 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

Somente 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 wcinterpretado como um único comando?

lobo-revo-gatos
fonte
3
Observe que os resultados diferem dependendo timeda palavra-chave shell ou /usr/bin/time. Pode haver vários conjuntos de descritores envolvidos aqui (o shell e os anexados a um timeprocesso). E não vamos esquecer os implícitos no ()subshell. ( Esperando por um especialista do bash : p)
John WH Smith

Respostas:

24

Em ksh, bashe zsh, timenão é um comando (embutido ou não), é uma palavra reservada no idioma como forou while.

É usado para cronometrar um pipeline 1 .

Dentro:

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

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

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

E relate estatísticas de tempo para isso.

Dentro:

time cmd > output 2> error

É o mesmo, você está cronometrando o cmd > output 2> errorcomando e as estatísticas de cronometragem ainda continuam 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 que o stderr do shell seja redirecionado para timing-outputantes da construção da hora (novamente, não do comando ) ser usada (aqui para a hora cmd > output 2> error 3>&-).

Você também pode executar essa timeconstrução em um subshell que tenha seu stderr redirecionado:

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

Mas esse subshell não é necessário aqui, você só precisa que o stderr seja redirecionado no momento em que a timeconstrução é invocada.

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

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

Mas cuidado, o formato pode ser diferente e o stderr de ambos timee cmdserá mesclado error-and-timing-output.

Além disso, o timecomando, ao contrário da timeconstrução, não pode cronometrar pipelines ou comandos ou funções compostas ou embutidos no shell ...

Se fosse um comando interno, ele poderá cronometrar invocações ou funções internas de função, mas não poderá cronometrar redirecionamentos, pipelines ou comandos compostos.


1 Observe que bashpossui (o que pode ser considerado) um erro pelo qual time (cmd) 2> file(mas não time cmd | (cmd2) 2> filepor exemplo) redireciona a saída de temporização parafile

Stéphane Chazelas
fonte
Bem, costumo enfatizar que timeé uma palavra-chave, não um shell embutido.
cuonglm
Obrigado pela dica sobre o uso 'time'para obter o executável - mais prático do que escrever /usr/bin/time(ou até command time:-)).
Alexis
@alexis também \time.
CTRL-ALT-DELOR
7

Não há nenhum comando chamado time wc, timee wcsão separados palavra no escudo.

Agora, geralmente existem dois programas separados nomeados time, um é a palavra-chave shell, outro é o comando externo . Em shells, que timeé uma palavra-chave shell, quando você digita time wc ..., o shell usa sua palavra-chave em timevez do utilitário de tempo externo .

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

time wc file > wc.out 2>&1

afeta wcapenas.

Quando você usa o comando composto(list) :

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

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


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

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

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

/usr/bin/time wc file > wc.out 2>&1
cuonglm
fonte
Existe alguma diferença prática entre usar timee /usr/bin/time? Os executáveis ​​estão sendo chamados funcionalmente iguais?
Hashim
2

Porque timevocê está executando o bash builtin. O Bash processa-o de maneira tão especial.

Se você usar o timebinário real , ele atuará exatamente da maneira que você espera:

/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
pressa
fonte
9
Se fosse um builtin, não seria um problema. O problema é que é uma palavra - chave no idioma. time cmd > outputvezes o cmd > outputcomando e time foo | barvezes foo | bar.
Stéphane Chazelas
1

Não é timeque escreve a informação da hora. O builtin timefaz com que o shell escreva isso após a conclusão do comando. Mas o redirecionamento afeta apenas o comando.

No (time ...)caso, o redirecionamento é aplicado a todo o subshell.

Hauke ​​Laging
fonte
0

Como o tempo é incorporado ao shell, ele grava no stderr do shell , e não no stderr do comando.

Usar parênteses força todo o comando em um shell filho cujo stderr pode ser redirecionado.

o uso de colchetes produz um resultado semelhante sem realmente iniciar um subshell

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

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

darkonc
fonte