Eu quero time
um comando que consiste em dois comandos separados com uma saída de tubulação para outra. Por exemplo, considere os dois scripts abaixo:
$ cat foo.sh
#!/bin/sh
sleep 4
$ cat bar.sh
#!/bin/sh
sleep 2
Agora, como posso time
relatar o tempo gasto foo.sh | bar.sh
(e sim, eu sei que o tubo não faz sentido aqui, mas este é apenas um exemplo)? Funciona como esperado se eu executá-los sequencialmente em um subshell sem canalizar:
$ time ( foo.sh; bar.sh )
real 0m6.020s
user 0m0.010s
sys 0m0.003s
Mas não consigo fazê-lo funcionar durante a tubulação:
$ time ( foo.sh | bar.sh )
real 0m4.009s
user 0m0.007s
sys 0m0.003s
$ time ( { foo.sh | bar.sh; } )
real 0m4.008s
user 0m0.007s
sys 0m0.000s
$ time sh -c "foo.sh | bar.sh "
real 0m4.006s
user 0m0.000s
sys 0m0.000s
Eu li uma pergunta semelhante ( como executar o tempo em vários comandos E gravar a saída do tempo no arquivo? ) E também tentei o time
executável independente :
$ /usr/bin/time -p sh -c "foo.sh | bar.sh"
real 4.01
user 0.00
sys 0.00
Nem funciona se eu criar um terceiro script que execute apenas o pipe:
$ cat baz.sh
#!/bin/sh
foo.sh | bar.sh
E então cronometre isso:
$ time baz.sh
real 0m4.009s
user 0m0.003s
sys 0m0.000s
Curiosamente, não parece que time
sai assim que o primeiro comando é concluído. Se eu mudar bar.sh
para:
#!/bin/sh
sleep 2
seq 1 5
E, time
novamente, eu esperava que a time
saída fosse impressa antes do seq
mas não é:
$ time ( { foo.sh | bar.sh; } )
1
2
3
4
5
real 0m4.005s
user 0m0.003s
sys 0m0.000s
Parece time
que não conta o tempo que levou para executar, bar.sh
apesar de esperar a conclusão antes de imprimir o relatório 1 .
Todos os testes foram executados em um sistema Arch e usando o bash 4.4.12 (1) - release. Só posso usar o bash para o projeto que faz parte, mesmo que zsh
algum shell poderoso possa contorná-lo, isso não será uma solução viável para mim.
Então, como posso obter o tempo que um conjunto de comandos canalizados levou para executar? E, enquanto estamos nisso, por que não funciona? Parece que time
sai imediatamente assim que o primeiro comando é concluído. Por quê?
Eu sei que posso obter os momentos individuais com algo assim:
( time foo.sh ) 2>foo.time | ( time bar.sh ) 2> bar.time
Mas ainda gostaria de saber se é possível cronometrar a coisa toda como uma única operação.
1 Este não parece ser um problema de buffer, eu tentei executar os scripts com unbuffered
e stdbuf -i0 -o0 -e0
e os números ainda foram impressas antes da time
saída.
Respostas:
Ele está trabalhando.
As diferentes partes de um pipeline são executadas simultaneamente. A única coisa que sincroniza / serializa os processos no pipeline é a E / S, ou seja, um processo gravando no próximo processo no pipeline e o próximo processo lendo o que o primeiro grava. Além disso, eles estão executando independentemente um do outro.
Como não há leitura ou gravação entre os processos em seu pipeline, o tempo necessário para executá-lo é o da
sleep
chamada mais longa .Você também pode ter escrito
Terdon postou alguns scripts de exemplo levemente modificados no chat :
e
A consulta foi "por que
time ( sh foo.sh | sh bar.sh )
retornar 4 segundos em vez de 3 + 3 = 6 segundos?"Para ver o que está acontecendo, incluindo o tempo aproximado em que cada comando é executado, é possível fazer isso (a saída contém minhas anotações):
Assim, para concluir, o gasoduto leva 4 segundos, não 6, devido ao buffer da saída das duas primeiras chamadas para
echo
emfoo.sh
.fonte
ksh93
aguardam apenas o último componente do pipeline (sleep 3 | sleep 1
durariam 1 segundo). o shell Bourne não temtime
palavra-chave, masksh93
, quando executado comtime
, todos os componentes são esperados.sleep 10 | sleep 1
leva um segundo enquantotime sleep 10 | sleep 1
leva 10 segundos no ksh93. No shell Bournetime sleep 10 | sleep 1
, levaria um segundo, mas você obteria a saída de tempo (sleep 10
somente e de/usr/bin/time
) fora do azul 9 segundos depois.time
cronometra corretamente o pipeline, mas altera o comportamento do shell no ksh93.(sleep 10 | sleep 1)
leva 1 segundo,time (sleep 10 | sleep 1)
leva 10 segundos.{ (sleep 10 | sleep 1); echo x; }
saídasx
após 1 segundo,time { (sleep 10 | sleep 1); echo x; }
saídasx
após 10 segundos. Mesmo se você colocar esse código em uma função e cronometrar a função.ksh93
como emzsh
(-o promptsubst
aqui), você pode fazertypeset -F SECONDS
para obter um menos aproximada número de segundos (POSIXsh
não temSECONDS
)Este seria um exemplo melhor?
Os scripts busyloop por 3 e 4 segundos (resp.), Levando um total de 4 segundos em tempo real por causa da execução paralela e 7 segundos de tempo da CPU. (pelo menos aproximadamente.)
Ou isto:
Como não são executados em paralelo, o tempo total gasto é de 5 segundos. Tudo é gasto dormindo, portanto, não é usado tempo de CPU.
fonte
Se você tiver,
sysdig
poderá inserir rastreadores em pontos arbitrários, supondo que você possa modificar o código para adicionar as gravações necessárias em/dev/null
(mas isso falha no seu requisito de "operação única") e registra as coisas por meio de
e então você provavelmente precisará de um cinzel sysdig para exportar apenas as durações
que uma vez salvo em seu
sysdig/chisels
diretório como o arquivospantagduration.lua
pode ser usado comoOu você pode brincar com
csysdig
ou com a saída JSON.fonte