Quiero timeun comando que consta de dos comandos separados con una salida de tubería a otra. Por ejemplo, considere los dos scripts a continuación:
$ cat foo.sh
#!/bin/sh
sleep 4
$ cat bar.sh
#!/bin/sh
sleep 2
Ahora, ¿cómo puedo timeinformar el tiempo que lleva foo.sh | bar.sh(y sí, sé que la tubería no tiene sentido aquí, pero esto es solo un ejemplo)? Funciona como se esperaba si los ejecuto secuencialmente en una subshell sin tuberías:
$ time ( foo.sh; bar.sh )
real 0m6.020s
user 0m0.010s
sys 0m0.003s
Pero no puedo hacer que funcione cuando canalizo:
$ 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
He leído una pregunta similar ( ¿Cómo ejecutar el tiempo en múltiples comandos Y escribir la salida de tiempo en el archivo? ) Y también probé el timeejecutable independiente :
$ /usr/bin/time -p sh -c "foo.sh | bar.sh"
real 4.01
user 0.00
sys 0.00
Ni siquiera funciona si creo un tercer script que solo ejecuta la tubería:
$ cat baz.sh
#!/bin/sh
foo.sh | bar.sh
Y luego cronometra que:
$ time baz.sh
real 0m4.009s
user 0m0.003s
sys 0m0.000s
Curiosamente, no parece que timesalga tan pronto como se complete el primer comando. Si cambio bar.sha:
#!/bin/sh
sleep 2
seq 1 5
Y, de timenuevo, esperaba que la timesalida se imprimiera antes seqpero no lo es:
$ time ( { foo.sh | bar.sh; } )
1
2
3
4
5
real 0m4.005s
user 0m0.003s
sys 0m0.000s
Parece timeque no cuenta el tiempo que tardó en ejecutarse a bar.shpesar de esperar a que termine antes de imprimir su informe 1 .
Todas las pruebas se ejecutaron en un sistema Arch y utilizando bash 4.4.12 (1) -release. Solo puedo usar bash para el proyecto del que forma parte, por lo que incluso si zshalgún otro poderoso shell puede evitarlo, esa no será una solución viable para mí.
Entonces, ¿cómo puedo obtener el tiempo que tardó en ejecutarse un conjunto de comandos canalizados? Y, mientras estamos en eso, ¿por qué no funciona? Parece que timesale inmediatamente tan pronto como el primer comando ha terminado. ¿Por qué?
Sé que puedo obtener los tiempos individuales con algo como esto:
( time foo.sh ) 2>foo.time | ( time bar.sh ) 2> bar.time
Pero aún me gustaría saber si es posible cronometrar todo como una sola operación.
1 Esto no parece ser un problema de memoria intermedia, he intentado ejecutar los scripts con unbufferedy stdbuf -i0 -o0 -e0y el número todavía se imprimieron antes de la timesalida.

Respuestas:
Se está trabajando.
Las diferentes partes de una tubería se ejecutan simultáneamente. Lo único que sincroniza / serializa los procesos en la tubería es IO, es decir, un proceso que escribe en el siguiente proceso en la tubería y el siguiente proceso lee lo que escribe el primero. Aparte de eso, se ejecutan independientemente uno del otro.
Como no hay lectura ni escritura entre los procesos en su canalización, el tiempo que lleva ejecutar la canalización es el de la
sleepllamada más larga .También podrías haber escrito
Terdon publicó un par de scripts de ejemplo ligeramente modificados en el chat :
y
La consulta fue "¿por qué
time ( sh foo.sh | sh bar.sh )devuelve 4 segundos en lugar de 3 + 3 = 6 segundos?"Para ver qué sucede, incluido el tiempo aproximado que se ejecuta cada comando, uno puede hacer esto (la salida contiene mis anotaciones):
Por lo tanto, para concluir, la tubería es de 4 segundos, no 6, debido al amortiguamiento de la salida de las dos primeras llamadas a
echoenfoo.sh.fuente
ksh93solo esperan el último componente de la tubería (sleep 3 | sleep 1duraría 1 segundo). el shell Bourne no tienetimepalabra clave, peroksh93cuando se ejecuta contimetodos los componentes se esperan.sleep 10 | sleep 1toma un segundo mientras quetime sleep 10 | sleep 1toma 10 segundos en ksh93. En el shell Bournetime sleep 10 | sleep 1tomaría un segundo, pero obtendría la salida de tiempo (sleep 10solo y desde/usr/bin/time) de la nada 9 segundos después.timeveces la tubería correctamente, pero cambia el comportamiento del shell en ksh93.(sleep 10 | sleep 1)toma 1 segundo,time (sleep 10 | sleep 1)toma 10 segundos.{ (sleep 10 | sleep 1); echo x; }salidasxdespués de 1 segundo,time { (sleep 10 | sleep 1); echo x; }salidasxdespués de 10 segundos. Lo mismo si pones ese código en una función y cronometras la función.ksh93igual que enzsh(-o promptsubstaquí), puede hacertypeset -F SECONDSpara obtener un número de segundos menos aproximado (POSIXshno tieneSECONDS)¿Sería este un mejor ejemplo?
Las secuencias de comandos ocupan el bucle durante 3 y 4 segundos (resp.), Tomando un total de 4 segundos en tiempo real debido a la ejecución paralela y 7 segundos de tiempo de CPU. (al menos aproximadamente)
O esto:
Estos no se ejecutan en paralelo, por lo que el tiempo total es de 5 segundos. Todo se pasa durmiendo, por lo que no se usa tiempo de CPU.
fuente
Si lo tiene
sysdig, puede insertar trazadores en puntos arbitrarios, suponiendo que puede modificar el código para agregar las escrituras necesarias en/dev/null(pero eso no cumple con el requisito de "operación única") y luego graba cosas a través de
y probablemente necesitarás un cincel sysdig para exportar solo las duraciones
que una vez guardado en su
sysdig/chiselsdirectorio como el archivospantagduration.luapuede usarse comoO puede jugar con
csysdigo la salida JSON.fuente