Debian.pro

Блог для пользователей и администраторов Debian


time, /usr/bin/time и великий обман в bash

Для анализа того, сколько будут работать написанные мною скрипты я частенько использовал утилиту time. По крайней мере, я думал, что использовал утилиту time.
Как оказалось, в bash’e есть одноименная встроенная функция, которая представляет из себя сильно урезанный аналог настоящей утилиты time. Открыв однажды man time я немного удивился и навсегда запомнил этот момент.

Собственно, в чём соль. Из bash-евого time мы можем узнать по сути только время, которое было потрачено на выполнение команды системой. Да, пытливый ум почерпнет там несколько больше информации, но всё равно её набор сильно ограничен тремя строчками. Собственно:

user@host:~$ time ls
...
real 0m0.011s
user 0m0.000s
sys 0m0.000s

Ну да, ценная информация. Но оригинальная утилита time была написана для того, чтобы можно было посмотреть сколько ресурсов заняло у системы выполнение команды. При том любых ресурсов.
В частности, ресурсы процессора, память, операции ввода-вывода, количество swap-событий и тучу другой диагностической информации, вплоть до exit-кода.
Такой мощный инструмент уже намного лучше подходит для диагностики ресурсоёмкости. Описывать заново мануал я вам не собираюсь (в man time можно подсмотреть все ресурсы, которые оно может подсчитывать), а попробую просто вкратце описать, как её использовать. А главное — развеять один из мифов тех линуксоидов, который пришли к нам уже во владения bash’a и ничего другого не видели =)

Начнем с самого простого, научимся её запускать:

user@host:~$ /usr/bin/time ls
...
0.01user 0.00system 0:00.01elapsed 100%CPU (0avgtext+0avgdata 5024maxresident)k
0inputs+0outputs (0major+382minor)pagefaults 0swaps

Или так(все же помнят, что если перед командой поставить back-слэш, то bash сразу полезет искать бинарник в $PATH, игнорируя конфиг с алиасами и встроенные функции?):

user@host:~$ \time ls
...
0.01user 0.00system 0:00.01elapsed 100%CPU (0avgtext+0avgdata 5024maxresident)k
0inputs+0outputs (0major+382minor)pagefaults 0swaps

time (в любой вариации) выводит себя любимого на STDERR. Нагло воспользуемся этим, чтобы не наблюдать на экране мусор от вывода:

user@host:~$ \time cat /dev/urandom 1>/dev/null
^CCommand terminated by signal 2
0.00user 1.14system 0:01.14elapsed 99%CPU (0avgtext+0avgdata 2544maxresident)k
0inputs+0outputs (0major+206minor)pagefaults 0swaps

Здесь видно, что на экран попало только сообщение о том, что команда была прервана по ^C, и вывод утилиты time.

Ну и перейдем к самому важному. К формату вывода. Самое ценное для нас, что умеет \time — вывести реальное физическое время, которое прошло с момента запуска команды до момента её выполнения (по крайней мере, нам об этом говорит название утилиты =) ):

user@host:~$ \time -f %E ls 1>/dev/null
0:00.01
user@host:~$ \time -f %E sleep 5 1>/dev/null
0:05.00

-f %E — задало формат вывода для time. E = реальное время, % — метасимвол. Остальные переменные, опять же, есть в мане.
Ну и попробуем чуть более сложный формат, чтобы уметь с ним работать:

user@host:~$ \time -f "Command: %C \nExit code: %X \nReal time: %E" sleep 1 1>/dev/null
Command: sleep 1
Exit code: 0
Real time: 0:01.00

Собственно, удачного дебага.


Комментарии (5):

  1. GoTLiuM :

    Все таки время стирает в памяти порой очень важные вещи

  2. Ещё «настоящий» time из bash можно запустить так: user@host:~$ ‘time’ ls

    В шестом абзаце, видимо, опечатка: «…в man *date* можно подсмотреть…»

  3. Да, опечатка, спасибо)
    Пока писал — постоянно их путал.

  4. Alukardd :

    Хотя я редко пользуюсь функционалом time и мне в принципе хватало того что даёт встроенная функция, но тем не менее, полезно знать на что способен системный time. Спасибо.

    Правда для интерактивной работы у меня стоит по умолчанию zsh, а в нём нормальный time :-)

  5. virens :

    Ого, а я и не знал :-) Спасибо за пост.
    Обычно хватает просто time, когда пытаешься что-то протестировать.
    Хотя /usr/bin/time конечно лучше.

Написать комментарий