Ich möchte time
einen Befehl, der aus zwei separaten Befehlen besteht, mit einem Piping-Ausgang zu einem anderen. Betrachten Sie beispielsweise die beiden folgenden Skripte:
$ cat foo.sh
#!/bin/sh
sleep 4
$ cat bar.sh
#!/bin/sh
sleep 2
Wie kann ich time
nun die Zeit foo.sh | bar.sh
abrufen, die vergangen ist (und ja, ich weiß, dass die Pipe hier keinen Sinn ergibt, aber dies ist nur ein Beispiel)? Es funktioniert wie erwartet, wenn ich sie nacheinander in einer Subshell ohne Piping ausführe:
$ time ( foo.sh; bar.sh )
real 0m6.020s
user 0m0.010s
sys 0m0.003s
Aber ich kann es nicht zum Laufen bringen:
$ 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
Ich habe eine ähnliche Frage durchgelesen ( wie man Zeit mit mehreren Befehlen ausführt UND die Zeitausgabe in eine Datei schreibt? ) Und auch die eigenständige time
ausführbare Datei ausprobiert :
$ /usr/bin/time -p sh -c "foo.sh | bar.sh"
real 4.01
user 0.00
sys 0.00
Es funktioniert nicht einmal, wenn ich ein drittes Skript erstelle, das nur die Pipe ausführt:
$ cat baz.sh
#!/bin/sh
foo.sh | bar.sh
Und dann mal das:
$ time baz.sh
real 0m4.009s
user 0m0.003s
sys 0m0.000s
Interessanterweise sieht es nicht so time
aus, als würde es beendet, sobald der erste Befehl ausgeführt wird. Wenn ich bar.sh
zu:
#!/bin/sh
sleep 2
seq 1 5
Und time
andererseits hatte ich erwartet, dass die time
Ausgabe vor dem gedruckt wird, seq
aber es ist nicht:
$ time ( { foo.sh | bar.sh; } )
1
2
3
4
5
real 0m4.005s
user 0m0.003s
sys 0m0.000s
Sieht time
so aus, als würde die Ausführungszeit nicht gezählt, bar.sh
obwohl gewartet wurde, bis der Bericht gedruckt wurde 1 .
Alle Tests wurden auf einem Arch-System und mit bash 4.4.12 (1) -release durchgeführt. Ich kann bash nur für das Projekt verwenden. Dies ist ein Teil davon. Selbst wenn zsh
eine andere leistungsstarke Shell es umgehen kann, ist dies für mich keine praktikable Lösung.
Wie kann ich also die Zeit ermitteln, die eine Reihe von Pipe-Befehlen für die Ausführung benötigt hat? Und wenn wir schon dabei sind, warum funktioniert es dann nicht? Es sieht so time
aus, als würde es sofort beendet, sobald der erste Befehl beendet ist. Warum?
Ich weiß, ich kann die einzelnen Zeiten mit so etwas abrufen:
( time foo.sh ) 2>foo.time | ( time bar.sh ) 2> bar.time
Aber ich würde immer noch gerne wissen, ob es möglich ist, das Ganze als einzelne Operation zu messen.
1 Dies scheint kein Pufferproblem zu sein. Ich habe versucht, die Skripte mit unbuffered
und auszuführen, stdbuf -i0 -o0 -e0
und die Zahlen wurden vor der time
Ausgabe noch gedruckt .
Antworten:
Es ist zu arbeiten.
Die verschiedenen Teile einer Pipeline werden gleichzeitig ausgeführt. Das einzige, was die Prozesse in der Pipeline synchronisiert / serialisiert, ist IO, dh ein Prozess schreibt auf den nächsten Prozess in der Pipeline und der nächste Prozess liest, was der erste schreibt. Ansonsten arbeiten sie unabhängig voneinander.
Da zwischen den Prozessen in Ihrer Pipeline weder Lese- noch Schreibvorgänge stattfinden, dauert die Ausführung der Pipeline den längsten
sleep
Aufruf.Du hättest genauso gut schreiben können
Terdon hat ein paar leicht modifizierte Beispielskripte im Chat gepostet :
und
Die Abfrage lautete "Warum werden
time ( sh foo.sh | sh bar.sh )
4 Sekunden anstatt 3 + 3 = 6 Sekunden zurückgegeben?"Um zu sehen, was passiert, einschließlich der ungefähren Zeit, zu der jeder Befehl ausgeführt wird, kann man dies tun (die Ausgabe enthält meine Anmerkungen):
Abschließend nimmt die Pipeline aufgrund der Pufferung der Ausgabe der ersten beiden Aufrufe von
echo
in 4 Sekunden und nicht 6 Sekunden in Anspruchfoo.sh
.quelle
ksh93
nur auf die letzte Komponente der Pipeline warten (sleep 3 | sleep 1
würde 1 Sekunde dauern). Die Bourne-Shell hat keintime
Schlüsselwort, aberksh93
wenn sie mit ausgeführt wirdtime
, wird auf alle Komponenten gewartet.sleep 10 | sleep 1
eine Sekundetime sleep 10 | sleep 1
dauert, während es in ksh93 10 Sekunden dauert. In der Bourne-Shelltime sleep 10 | sleep 1
würde es eine Sekunde dauern, aber Sie würden die Zeitausgabe (sleep 10
nur für und von/usr/bin/time
) 9 Sekunden später aus heiterem Himmel erhalten.time
korrekt mal die Pipeline, aber ändert das Verhalten der Shell in ksh93.(sleep 10 | sleep 1)
dauert 1 Sekunde,time (sleep 10 | sleep 1)
dauert 10 Sekunden.{ (sleep 10 | sleep 1); echo x; }
Ausgängex
nach 1 Sekunde,time { (sleep 10 | sleep 1); echo x; }
Ausgängex
nach 10 Sekunden. Dasselbe gilt, wenn Sie diesen Code in eine Funktion einfügen und die Funktion zeitlich festlegen.ksh93
wie inzsh
(-o promptsubst
hier) tun könnentypeset -F SECONDS
, um eine weniger ungefähre Anzahl von Sekunden zu erhalten (POSIXsh
hat keineSECONDS
)Wäre das ein besseres Beispiel?
Die Skripte werden 3 bzw. 4 Sekunden lang ausgeführt, was aufgrund der parallelen Ausführung insgesamt 4 Sekunden in Echtzeit und 7 Sekunden CPU-Zeit in Anspruch nimmt. (Zumindest ungefähr.)
Oder dieses:
Diese laufen nicht parallel, die Gesamtzeit beträgt also 5 Sekunden. Es wird nur geschlafen, daher wird keine CPU-Zeit benötigt.
quelle
Wenn Sie haben
sysdig
, können Sie Tracer an beliebigen Stellen einfügen , vorausgesetzt, Sie können den Code ändern, um die erforderlichen Schreibvorgänge hinzuzufügen/dev/null
(aber das scheitert an Ihrer "Einzeloperation" -Anforderung) und zeichnet dann Dinge über auf
und dann brauchst du wahrscheinlich einen sysdig-meißel, um nur die dauer zu exportieren
die einmal in Ihrem
sysdig/chisels
Verzeichnis als die Datei gespeichert wurde,spantagduration.lua
kann als verwendet werdenOder Sie können mit
csysdig
oder mit der JSON-Ausgabe herumspielen.quelle