Каков элегантный способ, которым сценарий-оболочка умножает ** сам ** (на самом деле)? - PullRequest
3 голосов
/ 14 августа 2011

У меня есть скрипт bash, назовите его Exp, который выполняет вычислительный эксперимент, и я хочу иметь результат

time Exp

внутри самого скрипта: сначала он всегда должен быть сделано (и полагаться на ввод «time Exp» недостаточно - для критического случая, когда это нужно вам (или пользователю !!), это будет забыто), а во-вторых, сам скрипт Exp должен хранить егов файле.

Написание скрипта-обёртки (которое вызывает "время Exp"), кажется, делает обычную работу с Exp невозможной из-за разрушения параметров и ввода / вывода командой time.

Но на самом деле все, что нужно, - это получить доступ к данным в самом Exp той универсальной записи (которая также доступна для ps), которая только что напечатана временем!Вот почему я прошу «элегантное» решение, а не просто сначала как-то сохранить дату в Exp, и, наконец, до выхода, вычислить разницу.Но просто имитирую, что команда времени делает в Exp.Я думаю, что это было бы полезно во многих других ситуациях.

Ответы [ 3 ]

7 голосов
/ 14 августа 2011

Поскольку POSIX определяет утилиту time для записи своего результата при стандартной ошибке, вам нужно знать только о том, что существует встроенная bash time, которая ведет себя немного по-другому .

$ time sleep 1

real    0m1.004s
user    0m0.001s
sys     0m0.002s
$ time sleep 1 2>xyz

real    0m1.005s
user    0m0.001s
sys     0m0.003s
$ (time sleep 1) 2>xyz
$ cat xyz

real    0m1.005s
user    0m0.001s
sys     0m0.002s
$ /usr/bin/time sleep 1 2>xyz
$ cat xyz
        1.00 real         0.00 user         0.00 sys
$ 

Тестирование показано на MacOS X 10.7. Обратите внимание на разницу в формате вывода между встроенной и внешней версиями команды time. Также обратите внимание, что в формате под-оболочки встроенный time перенаправляется нормально, но в простом случае перенаправление вывода после встроенного time не отправляет вывод в то же место, где остальные стандартной ошибки идет в.

Итак, эти наблюдения позволяют вам написать свой элегантный сценарий. Вероятно, самый простой способ - обернуть существующий скрипт как функцию, а затем вызвать эту функцию через встроенный time.

Оригинальный сценарий

# This is what was in the Exp script before.
# It echoes its name and its arguments to both stdout and stderr
echo "Exp $*"
echo "Exp $*" 1>&2

Пересмотренный скрипт

log=./Exp.log

Exp()
{
    # This is what was in the Exp script before.
    # It echoes its name and its arguments to both stdout and stderr
    echo "Exp $*"
    echo "Exp $*" 1>&2
}

(time Exp "$@") 2>> $log

Обратите внимание на осторожное использование "$@" при вызове функции Exp для сохранения отдельных аргументов, передаваемых в командной строке, и одинаково преднамеренное использование $* внутри функции ( который теряет отдельные аргументы, но только для иллюстративных целей).

Возможная проблема

Единственная проблема, связанная с этим, заключается в том, что выходные данные об ошибках исходной команды также передаются в тот же файл журнала, что и информация о времени. Это может быть решено, но включает в себя хитрое многократное перенаправление , которое скорее запутает, чем поможет. Однако, к сведению, это прекрасно работает:

log=./Exp.log

Exp()
{
    # This is what was in the Exp script before.
    # It echoes its name and its arguments to both stdout and stderr
    echo "Exp $*"
    echo "Exp $*" 1>&2
}

exec 3>&2

(time Exp "$@" 2>&3 ) 2>> $log
1 голос
/ 14 августа 2011

Два вышеупомянутых решения вызывают команду времени. У меня есть сомнения, что даже само разработанный @Jonathan Leffler действительно функционально эквивалентен оригинальному сценарию: кажется, позаботиться о выводе на стандартный вывод и стандартную ошибку, но как это ведет себя w.r.t. символические ссылки, которые нужно проверить (и это будет не так просто --- есть много тонкостей относительно путей, особенно когда они содержат ссылки). И я думаю, что W.r.t. неприятный бизнес цитирования это определенно меняет семантику исходного скрипта, что делает необходимым добавить еще один слой цитаты к параметрам (если, например, скрипт выполняется удаленно, и нужно иметь кавычки).

С этими двумя проблемами, обработкой путей и символических ссылок и цитаты, мы получили В прошлом было много проблем, и эти ошибки очень трудно уловить: часто скрипты, которые мы пишем, используют сложные другие скрипты из многих пакетов математики / информатики, где нужно обрабатывать буквально сотни специально установленных пакетов, у каждого свои странные особенности, и поэтому мы хотим избежать как можно больше добавляя дальнейшие осложнения.

Решение, которое я нашел с помощью @Arne, проще; увидеть Как использовать модификатор S-output с командой Unix / Linux ps?

Нужно просто добавить строку

ps p $$ k time S | tail -n 1 | tr -s '[:space:]' | cut -d ' ' -f 4 > log-file

к сценарию, когда требуется сохранить (общее) время процесса в лог-файле. Не знаю, где команда времени получает настенные часы и системное время, но для настенных часов, возможно, нужно сделать вычитание времени; и не знаю о системном времени, но оно должно быть где-то.

1 голос
/ 14 августа 2011

Вы можете просто добавить следующее в качестве первой строки скрипта:

test -z "$TIMED" && TIMED=yes exec time $0 $@

Это не будет время выполнения, если в среде установлено значение TIMED, поэтому оно дает вам возможность подавить синхронизацию, еслиты хочешь.

...