Почему time ls | grep real
выводится это
real 0m0.002s
user 0m0.000s
sys 0m0.002s
вместо того, чтобы просто
real 0m0.002s
Если я наберу
echo -e 'real\t0m0.002s\nuser\t0m0.000s\nsys\t0m0.002s\n' | grep real
Я получаю то, что ожидаю, т.е.
real 0m0.002s
Я обнаружил, что это работает.
(time ls) 2>&1 > /dev/null | grep real
Что делает эта команда?
решение1
Почему time ls | grep real
не работает так, как ожидалось.
time
является bash
зарезервированным словом и ведет себя иначе, чем /usr/bin/time
.
Если зарезервированное слово времени предшествует конвейеру, сообщается прошедшее время, а также время пользователя и системы, затраченное на его выполнение.когда заканчивается трубопровод.
Источникbash(1): GNU Bourne-Again SHell - страница руководства Linux
Кроме того, time
выводит данные в стандартный поток ошибок, а не в стандартный поток вывода.
Используйте следующую команду:
(time ls) 2>&1 >/dev/null | grep real
Примечания:
Оператор
(
and)
объединяет командыtime
иls
вместе (чтобы эти команды оценивались в первую очередь)2>&1 >/dev/null
сначала перенаправляет stderr в stdout (который является каналом), а затем перенаправляет stdout в/dev/null
.Таким образом, stderr направляется в канал, а stdout никуда не направляется.
Дальнейшее чтение
- Алфавитный указатель командной строки Bash для Linux- Отличный справочник по всем вопросам, связанным с командной строкой Bash.
- кронштейны- Использование скобок для группировки и расширения выражений.
- синтаксис-перенаправление- Перенаправление и замена процессов.
- Справочное руководство Bash: Перенаправления
- Иллюстрированное руководство по перенаправлению [Bash Hackers Wiki]
решение2
Да, это работает, но требует небольшого перенаправления ввода-вывода :)
$ exec 3>&1 4>&2
{ time sleep 1 1>&3 2>&4; } 2>&1 |
grep 'real'
exec 3>&- 4>&-
Выход :
real 0m1,003s
Читатьbash FAQ#32иУчебник по перенаправлению ввода-вывода
Более простой способ:
$ TIMEFORMAT=%R
$ time sleep 1
1,003
решение3
Здесь есть две проблемы.
Первое и самое важное: «time» отправляет свой вывод в stderr (стандартный поток ошибок), а не в stdout, чтобы он не был перехвачен обычными командами перенаправления, если только вы не примете специальных мер.
Во-вторых, time — это не обычная программа, а специальная встроенная команда, интерпретируемая непосредственно оболочкой (для оболочек типа bash, csh, tcsh и т. д.). Таким образом, она работает нестандартным образом и измеряет время всей остальной части командной строки (если только вы не используете очень необычную оболочку).
Итак, чтобы получить желаемый результат (используя bash), вам нужно использовать:
(time ls) 2>&1 | grep real
Это запустит «ls» в подоболочке и хронометрирует ее [(время ls)] , отправить стандартную ошибку на стандартный вывод[ 2>&1 ], а затем отправить стандартный вывод в "grep" [|grep].
Последняя часть, которую вы использовали ">?dev/null" отбрасывает обычный вывод самой команды ls, в случае если он включал слово "real".
решение4
Похоже, что time
запись производится непосредственно в консоль, а не в stdout
, что означает, что вывод команды или конвейера не изменяется при добавлении time
.
Поэтому grep
он никогда не увидит отчет по времени, который в любом случае генерируется после grep
завершения, поскольку весь конвейер хронометрируется.