Écrivez la sortie de `time` dans un fichier, pourquoi des parenthèses sont-elles nécessaires?

18

timeécrit dans stderr, donc on pourrait supposer que l'ajout 2>&1à la ligne de commande devrait acheminer sa sortie vers stdout. Mais cela ne fonctionne pas:

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

Seulement avec des parenthèses, cela fonctionne:

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

Pourquoi des parenthèses sont-elles nécessaires dans ce cas? Pourquoi n'est-il pas time wcinterprété comme une seule commande?

loup-revo-chats
la source
3
Notez que les résultats diffèrent selon qu'il times'agit du mot clé shell ou /usr/bin/time. Il peut y avoir plusieurs ensembles de descripteurs impliqués ici (ceux du shell et ceux attachés à un timeprocessus). Et n'oublions pas ceux qu'implique le ()sous - shell. (en attente d'un spécialiste bash : p)
John WH Smith

Réponses:

24

Dans ksh, bashet zsh, timen'est pas une commande (intégrée ou non), c'est un mot réservé dans la langue comme forou while.

Il est utilisé pour chronométrer un pipeline 1 .

Dans:

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

Vous avez une syntaxe spéciale qui indique au shell d'exécuter cette ligne de tuyau:

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

Et rapportez des statistiques de synchronisation pour cela.

Dans:

time cmd > output 2> error

C'est la même chose, vous chronométrez la cmd > output 2> errorcommande et les statistiques de chronométrage vont toujours sur le stderr du shell.

Vous avez besoin:

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

Ou:

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

Pour que le stderr du shell soit redirigé vers timing-outputavant que la construction de temps (encore une fois, pas de commande ) soit utilisée (ici pour le temps cmd > output 2> error 3>&-).

Vous pouvez également exécuter cette timeconstruction dans un sous - shell dont le stderr est redirigé:

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

Mais ce sous-shell n'est pas nécessaire ici, vous n'avez besoin que de stderr pour être redirigé au moment où la timeconstruction est invoquée.

La plupart des systèmes ont également une timecommande. Vous pouvez l'invoquer en désactivant le timemot clé. Tout ce que vous avez à faire est de citer ce mot-clé, car les mots-clés ne sont reconnus comme tels que lorsqu'ils sont littéraux.

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

Mais attention, le format peut être différent et le stderr des deux timeet cmdsera fusionné error-and-timing-output.

De plus, la timecommande, contrairement à la timeconstruction, ne peut pas chronométrer les pipelines ou les commandes ou fonctions composées ou les commandes intégrées au shell ...

S'il s'agissait d'une commande intégrée, elle pourrait peut-être chronométrer les invocations de fonctions ou les commandes intégrées, mais elle ne pourrait pas chronométrer les redirections ou les pipelines ou les commandes composées.


1 Notez qu'il bashexiste (ce qui peut être considéré comme) un bogue par lequel time (cmd) 2> file(mais pas time cmd | (cmd2) 2> filepar exemple) redirige la sortie de synchronisation versfile

Stéphane Chazelas
la source
Eh bien, je souligne souvent ma tête pour me souvenir que timec'est un mot-clé, pas un shell intégré.
cuonglm
Merci pour l'astuce pour utiliser 'time'l'exécutable - plus pratique que d'écrire /usr/bin/time(ou même command time:-)).
alexis
@alexis aussi \time.
ctrl-alt-delor
7

Il n'y a pas de commande nommé time wc, timeet wcsont séparés mot dans la coquille.

Maintenant, il y a souvent deux programmes distincts nommés time, l'un est un mot-clé shell, un autre est une commande externe . Dans les shells qui timeest un mot-clé shell, lorsque vous tapez time wc ..., le shell a utilisé son mot-clé timeau lieu de l' utilitaire de temps externe .

Lorsque le shell utilise un timemot clé, il n'a pas besoin de fork () un nouveau processus, la timenorme actuelle et l'erreur standard ne sont pas modifiées. La partie redirection dans:

time wc file > wc.out 2>&1

affecte wcuniquement.

Lorsque vous utilisez la commande composée(list) :

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

le shell s'exécutait à l' time wc fileintérieur d'un sous-shell, (time wc file)était considéré comme une seule commande, et la partie de redirection affecte sa sortie standard et son erreur standard, qui incluent désormais les deux timeet wc.


Vous pouvez faire le même effet, sans avoir à forger un nouveau processus en utilisant une autre forme de commande de regroupement {list;}:

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

Si vous utilisez externe time, vous ne rencontrez pas ce problème, car il a été exécuté dans un nouveau processus:

/usr/bin/time wc file > wc.out 2>&1
cuonglm
la source
Existe-t-il une différence pratique entre l'utilisation de timeet /usr/bin/time? Les exécutables sont-ils appelés fonctionnellement de la même manière?
Hashim
2

Parce timeque vous exécutez est intégré à bash. Bash le traite d'une manière si spéciale.

Si vous utilisez un vrai timebinaire, il agira exactement de la façon dont vous vous y attendez:

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

Bien que la sortie de cette époque soit un peu différente:

 $ /usr/bin/time wc file > wc.out 
0.00user 0.00system 0:00.00elapsed ?%CPU (0avgtext+0avgdata1900maxresident)k
0inputs+8outputs (0major+82minor)pagefaults 0swaps
se ruer
la source
9
S'il s'agissait d'une fonction intégrée, ce ne serait pas un problème. Le problème est que c'est un mot - clé dans la langue. time cmd > outputfois la cmd > outputcommande, et time foo | barfois foo | bar.
Stéphane Chazelas
1

Ce n'est pas timecela qui écrit les informations de temps. Le programme intégré timefait écrire au shell ceci une fois la commande terminée. Mais la redirection affecte uniquement la commande.

Dans le (time ...)cas où la redirection est appliquée à l'ensemble du sous-shell.

Hauke ​​Laging
la source
0

Parce que le temps est un shell intégré, il écrit dans le stderr du shell , plutôt que dans le stderr de la commande.

L'utilisation de parenthèses force la commande entière dans un shell enfant dont stderr peut être redirigé.

l'utilisation de crochets produit un résultat similaire sans réellement démarrer un sous-shell

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

(oui, vous avez besoin du point-virgule)

darkonc
la source