Schreiben Sie die Ausgabe von `time` in eine Datei. Warum werden Klammern benötigt?

18

timeschreibt an stderr, so würde man annehmen, dass das Hinzufügen 2>&1zur Befehlszeile seine Ausgabe an leiten sollte stdout. Das geht aber nicht:

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

Nur mit Klammern funktioniert es:

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

Warum werden in diesem Fall Klammern benötigt? Warum wird es nicht time wcals ein einziger Befehl interpretiert ?

Wolf-Revo-Katzen
quelle
3
Beachten Sie, dass die Ergebnisse davon abhängen, ob timees sich um das Shell-Schlüsselwort handelt oder /usr/bin/time. Möglicherweise sind hier mehrere Deskriptorsätze beteiligt (die der Shell und die, die an einen timeProzess gebunden sind). Und vergessen wir nicht die von der ()Unterschale implizierten . ( Warten auf einen Bash-Spezialisten : p)
John WH Smith

Antworten:

24

In ksh, bashund zsh, timeist kein Befehl (builtin oder nicht), es ist ein reserviertes Wort in der Sprache wie foroder while.

Es wird verwendet, um eine Pipeline 1 zeitlich zu bestimmen .

Im:

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

Sie haben eine spezielle Syntax, die die Shell anweist, diese Pipeline auszuführen:

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

Und Timing-Statistiken dafür melden.

Im:

time cmd > output 2> error

Es ist dasselbe, Sie planen den cmd > output 2> errorBefehl und die Timing-Statistiken beziehen sich immer noch auf den Stderr der Shell.

Du brauchst:

{ time cmd > output 2> error; } 2> timing-output

Oder:

exec 3>&2 2> timing-output
time cmd > output 2> error 3>&-
exec 2>&3 3>&-

Damit das stderr der Shell umgeleitet wird, timing-outputbevor das time-Konstrukt (wieder kein Befehl ) verwendet wird (hier zur Zeit cmd > output 2> error 3>&-).

Sie können dieses timeKonstrukt auch in einer Subshell ausführen , deren stderr umgeleitet wird:

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

Diese Subshell ist hier jedoch nicht erforderlich. Sie müssen nur stderr zum Zeitpunkt des Aufrufs des timeKonstrukts umleiten .

Die meisten Systeme haben auch einen timeBefehl. Sie können dieses aufrufen, indem Sie das timeSchlüsselwort deaktivieren . Alles, was Sie tun müssen, ist, dieses Schlüsselwort in Anführungszeichen zu setzen, da Schlüsselwörter nur als solche erkannt werden, wenn sie wörtlich sind.

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

Beachten Sie jedoch, dass das Format und der Stderr von beiden unterschiedlich sein können timeund cmdzusammengeführt werden error-and-timing-output.

Außerdem kann der timeBefehl im Gegensatz zum timeKonstrukt keine Pipelines oder zusammengesetzten Befehle oder Funktionen oder Shell-Builtins zeitlich festlegen ...

Wenn es sich um einen eingebauten Befehl handelt, kann er möglicherweise Funktionsaufrufe oder eingebaute Befehle zeitlich festlegen, jedoch keine Zeitumleitungen, Pipelines oder Verbundbefehle.


1 Beachten Sie, dass bash(was als) ein Fehler vorliegt, bei dem time (cmd) 2> file(aber nicht time cmd | (cmd2) 2> filezum Beispiel) die Timing-Ausgabe an umgeleitet wirdfile

Stéphane Chazelas
quelle
Nun, ich timestreiche oft meinen Kopf, um mich daran zu erinnern, dass dies ein Schlüsselwort und keine eingebaute Shell ist.
Cuonglm
Vielen Dank für den Tipp, mit 'time'dem Sie die ausführbare Datei handlicher machen als mit Schreiben /usr/bin/time(oder sogar command time:-)).
Alexis
@alexis auch \time.
Strg-Alt-Delor
7

Es gibt keinen Befehl namens time wc, timeund wcwird getrennt Wort in der Schale.

Nun, da sind oft zwei separate Programm genannt time, die ein Shell - Schlüsselwort ist, ist ein weiterer externer Befehl . In Shells, bei denen timees sich um ein Shell-Schlüsselwort handelt, verwendete time wc ...die Shell bei der Eingabe das Schlüsselwort timeanstelle des externen Zeitdienstprogramms .

Wenn die Shell ein timeSchlüsselwort verwendet, muss der Prozess fork () nicht neu gestartet werden, und der aktuelle timeStandardein- und -fehler werden nicht geändert. Der Weiterleitungsteil in:

time wc file > wc.out 2>&1

betrifft wcnur.

Wenn Sie den zusammengesetzten Befehl verwenden(list) :

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

Die Shell wurde time wc filein einer Subshell ausgeführt und (time wc file)galt als einzelner Befehl. Der Umleitungsteil wirkt sich auf die Standardausgabe und den Standardfehler aus, die jetzt sowohl timeals auch enthalten wc.


Sie können den gleichen Effekt erzielen, ohne die Kosten für das Aufteilen eines neuen Prozesses zu verursachen, indem Sie eine andere Form des Gruppierungsbefehls verwenden {list;}:

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

Wenn Sie external verwenden time, tritt dieses Problem nicht auf, da es in einem neuen Prozess ausgeführt wurde:

/usr/bin/time wc file > wc.out 2>&1
cuonglm
quelle
Gibt es einen praktischen Unterschied zwischen der Verwendung von timeund /usr/bin/time? Werden die ausführbaren Dateien funktional gleich aufgerufen?
Hashim
2

Weil timeSie ausführen, ist bash builtin. Bash verarbeitet es auf so besondere Weise.

Wenn Sie echte timeBinärdateien verwenden, verhält es sich genau so, wie Sie es erwarten:

/usr/bin/time wc file > wc.out 2>&1

Obwohl die Ausgabe dieser Zeit etwas anders ist:

 $ /usr/bin/time wc file > wc.out 
0.00user 0.00system 0:00.00elapsed ?%CPU (0avgtext+0avgdata1900maxresident)k
0inputs+8outputs (0major+82minor)pagefaults 0swaps
eilen
quelle
9
Wenn es ein eingebautes wäre, wäre es kein Problem. Das Problem ist, dass es ein Schlüsselwort in der Sprache ist. time cmd > outputmal den cmd > outputbefehl und time foo | barmal foo | bar.
Stéphane Chazelas
1

Es ist nicht so, timedass die Zeitinformation geschrieben wird. Das Builtin veranlasst timedie Shell, dies zu schreiben, nachdem der Befehl abgeschlossen wurde. Die Umleitung wirkt sich jedoch nur auf den Befehl aus.

In diesem (time ...)Fall wird die Umleitung auf die gesamte Subshell angewendet.

Hauke ​​Laging
quelle
0

Da es sich bei time um eine eingebaute Shell handelt, wird in das stderr der Shell und nicht in das stderr des Befehls geschrieben.

Die Verwendung von Klammern zwingt den gesamten Befehl in eine untergeordnete Shell, deren stderr umgeleitet werden kann.

Wenn Sie geschweifte Klammern verwenden, erhalten Sie ein ähnliches Ergebnis, ohne eine Unterschale zu starten

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

(ja, du brauchst das Semikolon)

darkonc
quelle