У меня есть сценарий установки для окна Vagrant, где я использовал для измерения отдельных шагов time
. Теперь хотелось бы условно включить или отключить измерения времени.
Например, ранее строка выглядела бы так:
time (apt-get update > /tmp/last.log 2>&1)
Теперь я думал, что мог бы просто сделать что-то вроде этого:
MEASURE_TIME=true
[[ $MEASURE_TIME = true ]] && TIME="time --format=%e" || TIME=""
$TIME (apt-get update > /tmp/last.log 2>&1)
Но это не сработает:
syntax error near unexpected token `apt-get'
`$TIME (apt-get update > /tmp/last.log 2>&1)'
В чем здесь проблема?
Ответы:
Чтобы иметь возможность определить время подоболочки, вам нужно
time
ключевое слово , а не команда.time
Ключевое слово, часть языка, признаются только как таковые , когда вступили в буквальном смысле и в качестве первого слова команды (и в случаеksh93
, то следующая лексема не начинается с-
). Даже ввод"time"
не будет работать, не говоря уже о$TIME
(и будет восприниматься как вызовtime
команды вместо этого).Здесь вы можете использовать псевдонимы, которые раскрываются перед выполнением следующего цикла синтаксического анализа (поэтому оболочка может распознать это
time
ключевое слово):time
Ключевое слово не принимает параметры (за исключением-p
вbash
), но формат может быть установлен с помощьюTIMEFORMAT
переменнойbash
. (shopt
часть такжеbash
специфична, другие оболочки обычно не нуждаются в этом).источник
info -f bash --index-search=time
Хотя это
alias
один из способов сделать это, это также можно сделатьeval
- просто вы не столько хотите выполнитьeval
команду, сколько хотите объявитьeval
команду .Мне нравится
alias
es - я использую их все время, но мне больше нравятся функции - особенно их способность обрабатывать параметры и то, что их необязательно расширять в командную позицию, как это требуется дляalias
es.Так что я подумал, может быть, вы тоже захотите попробовать это:
$IFS
Немного в основном о$*
. Важно, что( subshell bit )
это также является результатом расширения оболочки, и поэтому для расширения аргументов в используемую мной синтаксическую строку"$*"
(неeval
"$@"
, кстати, если вы не уверены, что все аргументы могут быть объединены в пробелах) , Разделитель между аргументами in"$*"
является первым входящим байтом$IFS
, и поэтому может быть опасно продолжать работу без определения его значения. Таким образом, функция сохраняет$IFS
, устанавливает его в\n
ewline достаточно долго , чтобыset ... "$*"
в"$3"
,unset
оно что , сбрасывает его значение , если она ранее была.Вот небольшая демонстрация:
Видите ли, я поместил две команды в значение
$TIME
там - любое число в порядке - даже нет - но убедитесь, что оно экранировано и указано правильно - и то же самое относится и к аргументам_time()
. Все они будут объединены в одну командную строку, когда будут выполнены, но каждый аргумент получит свою собственную электронную\n
линию, и поэтому их можно относительно легко разложить. Или же вы можете объединить их все в одно, если хотите, и разделить их на электронные\n
линии или точки с запятой или что-то еще. Просто убедитесь, что один аргумент представляет команду, которую вы бы чувствовали комфортно, если бы вы вызывали ее в скрипте при вызове.\(
Например, хорошо, если это в конечном итоге с\)
. В основном нормальные вещи.Когда
eval
этот фрагмент получен, он выглядит так:И его результаты выглядят как ...
ВЫВОД
hash
Ошибка указывает на то, что я не имеют/usr/bin/time
установлен (потому что у меня нет) , иcommand
давайте нам знать это время работает. Трейлингset +x
- это еще одна команда, выполняемая послеtime
(что возможно) - важно быть осторожным с командами ввода, когдаeval
что-либо используется.источник
_time() { eval "$TIME $@"; }
? Недостатком использования функции является введение другой области видимости для переменных (не проблема для подоболочек)"$@"
расширении. Мне нравится один аргументeval
- иначе становится страшно. Хммм .... как ты имеешь в виду разные сферы хотя? Я думал, что это простоfunction
функции ...eval
соединяет свои аргументы перед выполнением, что вы пытаетесь сделать очень замысловатым способом. Я имел в виду, что_time 'local var=1; blah'
это сделало бы этоvar
локальным для этого_time
или_time 'echo "$#"'
напечатало бы$#
эту_time
функцию, а не функцию вызывающего.eval
сводит свои аргументы к пробелам - что, как вы говорите, именно то, что я делаю здесь - хотя это не было изначальным намерением. Я собираюсь это исправить.eval
В"$*"
отличие от"$@"
этого, я приобрел привычку после многих столкновений,command not found
когда арги были соединены не в том месте. В любом случае, хотя аргументы в конечном итоге выполняются в функции, я думаю, их достаточно просто развернуть при вызове. Это то, что я бы сделал в"$#"
любом случае.