Встроенная команда Bash ‘wait’ помогает мне понимать скрипты Bash как язык
Я просматривал исходный код основного скрипта в bash- проект http-monitoring, который недавно был опубликован на сайте социальных новостей. Общая идея заключалась в том, что он запускал несколько фоновых веб-запросов для параллельного выполнения и в конечном итоге составлял отчет о доступности различных веб-сайтов. Красиво, аккуратно и просто.
В основной части скрипта проекта srvmon
я увидел следующее:
# Выполняйте проверки параллельно для ключа в "$ {! urls [@]}" do value = $ {urls [$ key]} if [["$ (jobs | wc - l) "-ge $ {maxConcurrentCurls}]]; then # выполнить 12 команд curl при максимальном параллельном ожидании -n fi doRequest "$ key" "$ value" & donewait
Я заметил использование wait
в этих двух местах и был заинтригован; хотя я мог догадаться, что он делает, мне хотелось узнать больше. Немного покопавшись и поразмыслив над этим, меня поразило, что wait
помогает мне лучше понять происхождение сценариев оболочки и почему его часто неправильно понимают.
Встроенная функция wait
Во-первых, что такое wait
? Ну, это (обычно) встроенная команда, то есть команда, встроенная в сам исполняемый файл оболочки, а не существующая как отдельная программа. Описание заголовка таково: wait
«ожидает завершения задания и возвращает статус выхода». В статье в Википедии о нем отмечается, что он является встроенным, поскольку « должен знать таблицу заданий текущей среды выполнения оболочки », что имеет смысл с учетом его назначения.
Хотя в приведенном выше фрагменте кода есть несколько примеров, я подумал, что потрачу кофе на написание небольшого исследовательского скрипта под названием jobwait
, чтобы почувствовать, как ждать
может работать. Вот он:
#!/Usr/bin/env bashlog () {echo "$ (date +% H:% M:% S) $ * "} createjob () {local time = $ 1 local message = $ 2 (sleep" $ time "&& log" $ message ") & log" created job '$ message' ($ {time} s) PID = $! "} Main () {createjob 10 medium createjob 15 long createjob 5 short log" jobs created "wait -n && log" a job has completed "wait && log" all jobs are finished "} main" $ @ "
При запуске этого скрипта был получен следующий результат — обратите внимание на время для каждой записи журнала, которое показывает, когда была выпущена каждая запись журнала:
;./jobwait09: 03: 11 создано задание «среднее» (10 с) PID = 7267909: 03: 11 создано задание «длинное» (15 с) PID = 7268209: 03: 11 создано задание «короткое» (5 с) PID = 7268509: 03: Создано 11 рабочих мест 09: 03: 16 short09: 03: 16 работа завершена09: 03: 21 medium09: 03: 26 long09: 03: 26 все работы завершены;
Теперь ничего неожиданного в этом нет; тем не менее, было довольно приятно видеть, как все происходит в том порядке, в котором они происходили. Обратите внимание, что wait
также возвращает статус завершения задания, и с использованием &&
я игнорирую это здесь на свой страх и риск, но это всего лишь тестовый сценарий .
Параметр -n
заставляет wait
ждать завершения следующего задания, каким бы оно ни было. Итак, здесь мы видим, что запись журнала «задание завершено» создается, как только завершается одно из заданий — в данном случае «короткое».
оболочка как командная среда
Теперь мы знаем, что может делать wait
, я бы тоже хотел немного подумать о том, что он представляет .
Недавно мой обучающийся радар обнаружил различные разговоры, в которых мне казалось, что люди неправильно понимают, что такое сценарии оболочки. Это также появилось в этом месяце в ветке Lobster, где пользователь «pm» действительно помог мне понять, что разочаровывает в обсуждении «Bash против реального языка программирования».
Оболочка это как REPL для вашей операционной системы, интерактивная среда, в которой вы можете общаться с ней — управлять ресурсами, выполнять программы и так далее. В этом смысле язык этого разговора должен быть простым и иметь минимальный шум. Вам нужно просто ввести что-то, и это произойдет.
Более того, вы хотите указать значения с минимальными усилиями. Запустите программу, которая оперирует словом, или списком слов, или файлом, или списком файлов — вы же не хотите возиться с необходимостью цитировать эти слова в базовом регистре. И возможности, которые REPL предоставляет, чтобы вы могли в полной мере использовать ресурсы и программы, с которыми вы работаете, очень важны. Я думаю о конвейере Unix и перенаправлении ввода-вывода как о двух отличных примерах этого.
Эта ссылка на Unix напоминает мне замечательную статью, написанную в 1976 году одним из отцов Unix, Кеном Томпсоном. Это КОМАНДНЫЙ ЯЗЫК UNIX, который доступен в Интернет-архиве, но также в этом прекрасном репозитории стал более удобным для использования в различных форматах. Эта статья якобы является первой из когда-либо написанных о оболочке Unix, и ее приятно читать. В нем есть красиво простое введение в подоболочки, конвейеры и перенаправление ввода-вывода.
Возможно, более тонко, то, что мы знаем как источник сценариев оболочки сегодня, упоминается в названии статьи как «командный язык» ”, И вот что это такое. В этой статье много цитат, но я выберу здесь только одну, которая поможет мне подумать о том, что такое оболочка (и, косвенно, ее язык):
« Оболочка и выполняемые ею команды образуют язык выражений… [который] легко расширяется ”
Итак, этот REPL, наш интерфейс к операционной системе и ее ресурсам, — это командная среда, и наше прямое взаимодействие с ней осуществляется через командный язык, который был разработан, чтобы выразить наши намерения максимально простым и последовательным способом.
Вот еще одна цитата из раздела «ОБОЛОЧКА КАК КОМАНДА»:
« Оболочка — это просто еще одна команда, и, перенаправляя ее стандартный ввод, можно выполнять команды из файлов. »
Естественный переход к написанию сценариев
Итак, именно на этом этапе этого размышления мы начинаем переходить от REPL, где взаимодействие прямое… к набору команд, которые можно сохранить в файле и передается в оболочку, что, я думаю, можно рассматривать как косвенное взаимодействие.
Это, конечно, переход к сценариям, как к преднамеренным коллекциям элементов командного языка. И именно здесь wait
имеет большой смысл; возможно, его можно было бы использовать в интерактивном режиме, но мне он кажется более полезным как способ заставить некоторые вещи приостанавливаться, пока другие вещи завершаются, в непрямом режиме … в автоматическом режиме выполнения командного языка. Создание сценариев.
Переход от прямого использования командного языка (включая синтаксис, который позволяет нам объединять программы в конвейеры и управлять вводом и выводом) к написанию сценариев, таким образом, очень тонкий и кажется мне нравится естественный вывод. И функции, которые делают командную среду и ее язык настолько полезными в контексте прямого взаимодействия в REPL, — это именно те функции, которые доступны и для написания сценариев.
Для меня это суть сценариев оболочки и объясняет, почему это так. Хотя имеет смысл писать отдельные программы на любом подходящем языке — и, конечно же, следить за тем, чтобы эти программы вели себя предсказуемым и полезным образом в контексте командной среды, особенно в отношении STDIN, STDOUT и STDERR, — это абсолютно не дает у меня есть какой-либо смысл предлагать заменить сам сценарий оболочки на «современный язык» (что бы это ни значило).
Чтобы повторить (намеренно абсурдную) концепцию, упомянутую ранее в ветке лобстеров, попробуйте заменив вашу оболочку на REPL «современного языка», например, на Node.js или Python, и вы увидите, как упадет ваша производительность. Попробуйте использовать ресурсы операционной системы, выполнить программы и отфильтровать их вывод или отправить фоновые задания (и дождитесь
их завершения, прежде чем продолжить) — и вы скоро разорветесь.
Оболочка такая, какая есть не зря. Я доволен этим.