Как я могу условно передать подоболочку через «время»?


9

У меня есть сценарий установки для окна 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)'

В чем здесь проблема?


3
Вам просто нужно достичь целевой скорости, чтобы конденсатор потока работал;)
Златовласка

2
@goldilocks Вы имеете в виду магнит ? ;)
CVn

Ответы:


13

Чтобы иметь возможность определить время подоболочки, вам нужно time ключевое слово , а не команда.

timeКлючевое слово, часть языка, признаются только как таковые , когда вступили в буквальном смысле и в качестве первого слова команды (и в случае ksh93, то следующая лексема не начинается с -). Даже ввод "time"не будет работать, не говоря уже о $TIME(и будет восприниматься как вызов timeкоманды вместо этого).

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

shopt -s expand_aliases
alias time_or_not=
TIMEFORMAT=%E

MEASURE_TIME=true
[[ $MEASURE_TIME = true ]] && alias time_or_not=time

time_or_not (apt-get update > /tmp/last.log 2>&1)

time Ключевое слово не принимает параметры (за исключением -pв bash), но формат может быть установлен с помощью TIMEFORMATпеременной bash. ( shoptчасть также bashспецифична, другие оболочки обычно не нуждаются в этом).


Вы сказали: «Для того, чтобы рассчитать подоболочку, вам нужно ключевое слово time». Интересно, это поведение задокументировано где-то?
cuonglm

@cuonglm, см.info -f bash --index-search=time
Стефан Шазелас

3

Хотя это aliasодин из способов сделать это, это также можно сделать eval- просто вы не столько хотите выполнить evalкоманду, сколько хотите объявитьeval команду .

Мне нравится aliases - я использую их все время, но мне больше нравятся функции - особенно их способность обрабатывать параметры и то, что их необязательно расширять в командную позицию, как это требуется для aliases.

Так что я подумал, может быть, вы тоже захотите попробовать это:

_time() if   set -- "${IFS+IFS=\$2;}" "$IFS" "$@" && IFS='
';      then set -- "$1" "$2" "$*"; unset IFS
             eval "$1 $TIME ${3#"$1"?"$2"?}"
        fi

$IFSНемного в основном о $*. Важно, что ( subshell bit )это также является результатом расширения оболочки, и поэтому для расширения аргументов в используемую мной синтаксическую строку "$*" (не eval "$@", кстати, если вы не уверены, что все аргументы могут быть объединены в пробелах) , Разделитель между аргументами in "$*"является первым входящим байтом $IFS, и поэтому может быть опасно продолжать работу без определения его значения. Таким образом, функция сохраняет $IFS, устанавливает его в \newline достаточно долго , чтобы set ... "$*"в "$3", unsetоно что , сбрасывает его значение , если она ранее была.

Вот небольшая демонстрация:

TIME='set -x; time'
_time \( 'echo "$(echo any number of subshells)"' \
         'command -V time'                        \
         'hash time'                              \
      \) 'set +x'

Видите ли, я поместил две команды в значение $TIMEтам - любое число в порядке - даже нет - но убедитесь, что оно экранировано и указано правильно - и то же самое относится и к аргументам _time(). Все они будут объединены в одну командную строку, когда будут выполнены, но каждый аргумент получит свою собственную электронную \nлинию, и поэтому их можно относительно легко разложить. Или же вы можете объединить их все в одно, если хотите, и разделить их на электронные \nлинии или точки с запятой или что-то еще. Просто убедитесь, что один аргумент представляет команду, которую вы бы чувствовали комфортно, если бы вы вызывали ее в скрипте при вызове. \(Например, хорошо, если это в конечном итоге с \). В основном нормальные вещи.

Когда evalэтот фрагмент получен, он выглядит так:

+ eval 'IFS=$2;set -x; time (
echo "$(echo any number of subshells)"
command -V time
hash time
)
set +x'

И его результаты выглядят как ...

ВЫВОД

+++ echo any number of subshells
++ echo 'any number of subshells'
any number of subshells
++ command -V time
time is a shell keyword
++ hash time
bash: hash: time: not found

real    0m0.003s
user    0m0.000s
sys     0m0.000s
++ set +x

hashОшибка указывает на то, что я не имеют /usr/bin/timeустановлен (потому что у меня нет) , и commandдавайте нам знать это время работает. Трейлинг set +x- это еще одна команда, выполняемая после time (что возможно) - важно быть осторожным с командами ввода, когда evalчто-либо используется.


Есть ли причина не делать это _time() { eval "$TIME $@"; }? Недостатком использования функции является введение другой области видимости для переменных (не проблема для подоболочек)
Стефан Шазелас

@ StéphaneChazelas - из-за кавычек в "$@"расширении. Мне нравится один аргумент eval- иначе становится страшно. Хммм .... как ты имеешь в виду разные сферы хотя? Я думал, что это просто functionфункции ...
mikeserv

evalсоединяет свои аргументы перед выполнением, что вы пытаетесь сделать очень замысловатым способом. Я имел в виду, что _time 'local var=1; blah'это сделало бы это varлокальным для этого _timeили _time 'echo "$#"'напечатало бы $#эту _timeфункцию, а не функцию вызывающего.
Стефан Шазелас

@ StéphaneChazelas - у меня есть две вкладки для вас здесь. Правильно - evalсводит свои аргументы к пробелам - что, как вы говорите, именно то, что я делаю здесь - хотя это не было изначальным намерением. Я собираюсь это исправить. evalВ "$*"отличие от "$@"этого, я приобрел привычку после многих столкновений, command not foundкогда арги были соединены не в том месте. В любом случае, хотя аргументы в конечном итоге выполняются в функции, я думаю, их достаточно просто развернуть при вызове. Это то, что я бы сделал в "$#"любом случае.
mikeserv

+1 за приятный извращенный кусок хакерства ...
Стефан Шазелас
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.