Quiero time
un 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 time
informar 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 time
ejecutable 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 time
salga tan pronto como se complete el primer comando. Si cambio bar.sh
a:
#!/bin/sh
sleep 2
seq 1 5
Y, de time
nuevo, esperaba que la time
salida se imprimiera antes seq
pero no lo es:
$ time ( { foo.sh | bar.sh; } )
1
2
3
4
5
real 0m4.005s
user 0m0.003s
sys 0m0.000s
Parece time
que no cuenta el tiempo que tardó en ejecutarse a bar.sh
pesar 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 zsh
algú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 time
sale 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 unbuffered
y stdbuf -i0 -o0 -e0
y el número todavía se imprimieron antes de la time
salida.
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
sleep
llamada 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
echo
enfoo.sh
.fuente
ksh93
solo esperan el último componente de la tubería (sleep 3 | sleep 1
duraría 1 segundo). el shell Bourne no tienetime
palabra clave, peroksh93
cuando se ejecuta contime
todos los componentes se esperan.sleep 10 | sleep 1
toma un segundo mientras quetime sleep 10 | sleep 1
toma 10 segundos en ksh93. En el shell Bournetime sleep 10 | sleep 1
tomaría un segundo, pero obtendría la salida de tiempo (sleep 10
solo y desde/usr/bin/time
) de la nada 9 segundos después.time
veces 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; }
salidasx
después de 1 segundo,time { (sleep 10 | sleep 1); echo x; }
salidasx
después de 10 segundos. Lo mismo si pones ese código en una función y cronometras la función.ksh93
igual que enzsh
(-o promptsubst
aquí), puede hacertypeset -F SECONDS
para obtener un número de segundos menos aproximado (POSIXsh
no 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/chisels
directorio como el archivospantagduration.lua
puede usarse comoO puede jugar con
csysdig
o la salida JSON.fuente