„Погледајте овде“, каже он, показујући на један од знакова на екрану, „кладим се у свој црвени шешир да ако овде додамо оно што сам вам управо послао“ – показујући на други део кода – „грешка више неће бити бити приказан."
Мало збуњен и уморан, мењам сед изјаву на којој смо радили неко време, чувам датотеку и покрећем systemctl varnish reload
. Порука о грешци је нестала...
„Имејлови које сам разменио са кандидатом“, наставио је мој колега, док се његов осмех претвара у искрен осмех пун радости, „Одједном ми је синуло да је ово потпуно исти проблем!“
Како је све почело
Чланак претпоставља разумевање како басх, авк, сед и системд раде. Познавање лакова је пожељно али није обавезно.
Временске ознаке у исечцима су промењене.
Вриттен витх
Овај текст је превод оригинала објављеног на енглеском пре две недеље; превод
Сунце сија кроз панорамске прозоре још једног топлог јесењег јутра, шоља свеже скуваног пића са кофеином стоји поред тастатуре, омиљена симфонија звукова свира у слушалицама преко шуштања механичких тастатура, а први улазак у листа заосталих тикета на канбан табли заиграно сија са судбоносним насловом „Инвестигате варнисхрелоад сх: ецхо: И/О еррор ин стагинг” (Инвестигате „варнисхрелоад сх: ецхо: И/О еррор” ин стагинг). Када је у питању лак, грешке нема и не може бити, чак и ако не резултирају проблемима, као у овом случају.
За оне који нису упознати са
Као што наслов тикета сугерише, грешка се догодила на једном од сервера у фази, а пошто сам био уверен да рутирање лака у бини ради исправно, претпоставио сам да би то била мања грешка. Дакле, само порука која је ушла у већ затворени излазни ток. Узимам карту за себе, у пуном поверењу да ћу је означити спремном за мање од 30 минута, тапшам се по рамену да очистим таблу од следећег смећа и враћам се важнијим стварима.
Ударање у зид при 200 км/х
Отварање датотеке varnishreload
, на једном од сервера који користе Дебиан Стретцх, видео сам схелл скрипту мању од 200 редова.
Пролазећи кроз скрипту, нисам видео ништа што би могло да изазове проблеме када сам је покренуо више пута директно са терминала.
На крају крајева, ово је позорница, чак и ако се поквари, нико се неће жалити, па ... не превише. Покрећем скрипту и видим шта ће бити исписано на терминал, али грешке више нису видљиве.
Још неколико покрета да бих се уверио да не могу да репродукујем грешку без додатног напора, и почињем да схватам како да променим ову скрипту и да она и даље испушта грешку.
Може ли скрипта да блокира СТДОУТ (користећи > &-
)? Или СТДЕРР? Ни једно ни друго није успело на крају.
Очигледно је да системд на неки начин мења окружење за покретање, али како и зашто?
Укључујем вим и уређујем varnishreload
, додајући set -x
тачно испод схебанга, у нади да ће отклањање грешака у излазу скрипте бацити мало светла.
Фајл је поправљен, па поново учитавам лак и видим да је промена потпуно све покварила... Ауспух је потпуни неред, са тонама кода налик на Ц. Чак ни померање у терминалу није довољно да се пронађе где почиње. потпуно сам збуњен. Може ли режим за отклањање грешака утицати на рад програма који се покрећу у скрипти? Без зезања. Буг у љусци? Неколико могућих сценарија лети ми у глави као бубашвабе у различитим правцима. Шоља пића пуног кофеина се одмах испразни, брзи одлазак у кухињу по залихе и... идемо. Отварам сценарио и пажљивије погледам схебанг: #!/bin/sh
.
/bin/sh
- ово је само басх симболична веза, тако да се скрипта тумачи у ПОСИКС-компатибилном режиму, зар не? Није га било! Подразумевана љуска на Дебиан-у је дасх, што је управо оно /bin/sh
.
# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 24 2017 /bin/sh -> dash
Ради суђења, променио сам схебанг у #!/bin/bash
, уклоњено set -x
и покушао поново. Коначно, при накнадном поновном учитавању лака, у излазу се појавила толерантна грешка:
Jan 01 12:00:00 hostname varnishreload[32604]: /usr/sbin/varnishreload: line 124: echo: write error: Broken pipe
Jan 01 12:00:00 hostname varnishreload[32604]: VCL 'reload_20190101_120000_32604' compiled
Ред 124, ево га!
114 find_vcl_file() {
115 VCL_SHOW=$(varnishadm vcl.show -v "$VCL_NAME" 2>&1) || :
116 VCL_FILE=$(
117 echo "$VCL_SHOW" |
118 awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}' | {
119 # all this ceremony to handle blanks in FILE
120 read -r DELIM VCL_SHOW INDEX SIZE FILE
121 echo "$FILE"
122 }
123 ) || :
124
125 if [ -z "$VCL_FILE" ]
126 then
127 echo "$VCL_SHOW" >&2
128 fail "failed to get the VCL file name"
129 fi
130
131 echo "$VCL_FILE"
132 }
Али, како се испоставило, ред 124 је прилично празан и не занима га. Могао сам само да претпоставим да се грешка догодила као део вишелинијског померања на линији 116.
Шта је коначно записано у променљиву VCL_FILE
као резултат извршавања горње под-љуске?
На почетку шаље садржај променљиве VLC_SHOW
, креиран на линији 115, на следећу команду кроз цев. И шта се онда тамо дешава?
Прво, користи varnishadm
, који је део инсталационог пакета лака, да конфигуришете лак без поновног покретања.
подкоманда vcl.show -v
се користи за излаз целе ВЦЛ конфигурације наведене у ${VCL_NAME}
, на СТДОУТ.
Да бисте приказали тренутно активну ВЦЛ конфигурацију, као и неколико претходних верзија конфигурација рутирања лака које су још у меморији, можете користити команду varnishadm vcl.list
, чији ће излаз бити сличан следећем:
discarded cold/busy 1 reload_20190101_120000_11903
discarded cold/busy 2 reload_20190101_120000_12068
discarded cold/busy 16 reload_20190101_120000_12259
discarded cold/busy 16 reload_20190101_120000_12299
discarded cold/busy 28 reload_20190101_120000_12357
active auto/warm 32 reload_20190101_120000_12397
available auto/warm 0 reload_20190101_120000_12587
Вредност променљиве ${VCL_NAME}
постављен у другом делу сценарија varnishreload
на име тренутно активног ВЦЛ-а, ако постоји. У овом случају то ће бити „релоад_20190101_120000_12397“.
У реду, променљиво. ${VCL_SHOW}
садржи комплетну конфигурацију за лак, до сада јасан. Сада коначно разумем зашто цртица излаз са set -x
испоставило се да је тако покварен - укључивао је садржај настале конфигурације.
Важно је разумети да се комплетна ВЦЛ конфигурација често може спојити из више датотека. Коментари у Ц стилу се користе да дефинишу где је једна конфигурациона датотека укључена у другу, и управо о томе говори следећи ред исечка кода.
Синтакса за коментаре који описују укључене датотеке има следећи формат:
// VCL.SHOW <NUM> <NUM> <FILENAME>
Бројеви у овом контексту нису битни, занима нас назив фајла.
Дакле, шта се дешава у мочвари команди која почиње на линији 116?
Будимо искрени.
Команда се састоји из четири дела:
- Једноставно
echo
, који приказује вредност променљиве${VCL_SHOW}
echo "$VCL_SHOW"
awk
, који тражи линију (запис), где ће прво поље, након поделе текста, бити „//“, а друго ће бити „ВЦЛ.СХОВ“.
Авк ће исписати први ред који одговара овим обрасцима, а затим ће одмах прекинути обраду.awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
- Блок кода који чува вредности поља у пет варијабли, раздвојених размацима. Пета варијабла ФИЛЕ прима остатак линије. Коначно, последњи ехо исписује садржај променљиве
${FILE}
.{ read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
- Пошто су сви кораци од 1 до 3 затворени у подљуску, излаз вредности
$FILE
биће уписан у променљивуVCL_FILE
.
Као што коментар у реду 119 сугерише, ово служи јединој сврси поузданог руковања случајевима у којима ће се ВЦЛ позивати на датотеке са знаковима размака у њиховим именима.
Прокоментарисао сам оригиналну логику обраде за ${VCL_FILE}
и покушао да промени редослед команди, али то није довело ни до чега. Код мене је све функционисало чисто, а у случају покретања сервиса дало је грешку.
Чини се да се грешка једноставно не може поновити када се скрипта покреће ручно, док се процењених 30 минута већ завршило шест пута, а поред тога се појавио задатак вишег приоритета који је гурао остале случајеве у страну. Остатак недеље био је испуњен разним задацима и био је само мало разводњен разговором о сед-у и интервјуом са кандидатом. Проблем са грешком у varnishreload
неповратно изгубљен у песку времена.
Твој такозвани сед-фу... заправо... смеће
Следеће недеље је био један прилично слободан дан, па сам одлучио да поново узмем ову карту. Надао сам се да је у мом мозгу неки позадински процес све ово време тражио решење за овај проблем, а овај пут ћу свакако разумети шта није у реду.
Пошто прошли пут само промена кода није помогла, одлучио сам да га препишем почевши од 116. реда. У сваком случају, постојећи код је био глуп. И апсолутно нема потребе да се користи read
.
Гледајући поново грешку:
sh: echo: broken pipe
- у овој команди ехо је на два места, али претпостављам да је прво вероватнији кривац (па, или бар саучесник). Авк такође не улива поверење. И у случају да заиста јесте awk | {read; echo}
дизајн доводи до свих ових проблема, зашто га не заменити? Ова команда у једном реду не користи све карактеристике авк-а, па чак ни ову додатну read
у додатку.
Од прошле недеље је био извештај о sed
Желео сам да испробам своје новостечене вештине и поједноставим echo | awk | { read; echo}
у разумљивији echo | sed
. Иако ово дефинитивно није најбољи приступ да се ухвати грешка, мислио сам да ћу бар испробати свој сед-фу и можда научити нешто ново о проблему. Успут сам замолио свог колегу, писца сед талк-а, да ми помогне да смислим ефикаснији сед скрипт.
Испустио сам садржај varnishadm vcl.show -v "$VCL_NAME"
у датотеку тако да се могу фокусирати на писање сед скрипте без икаквих проблема са поновним покретањем услуге.
Кратак опис како тачно сед управља уносом може се наћи у n
експлицитно наведен као сепаратор линија.
У неколико пролаза, и уз савет мог колеге, написали смо сед скрипту која је дала исти резултат као цео оригинални ред 116.
Испод је пример датотеке са улазним подацима:
> cat vcl-example.vcl
Text
// VCL.SHOW 0 1578 file with 3 spaces.vcl
More text
// VCL.SHOW 0 1578 file.vcl
Even more text
// VCL.SHOW 0 1578 file with TWOspaces.vcl
Final text
Можда то није очигледно из горњег описа, али нас занима само први коментар // VCL.SHOW
, а може их бити неколико у улазним подацима. Због тога се оригинални авк прекида након првог меча.
# шаг первый, вывести только строки с комментариями
# используя возможности sed, определяется символ-разделитель с помощью конструкции '#' вместо обычно используемого '/', за счёт этого не придётся экранировать косые в искомом комментарии
# определяется регулярное выражение “// VCL.SHOW”, для поиска строк с определенным шаблоном
# флаг -n позаботится о том, чтобы sed не выводил все входные данные, как он это делает по умолчанию (см. ссылку выше)
# -E позволяет использовать расширенные регулярные выражения
> cat vcl-processor-1.sed
#// VCL.SHOW#p
> sed -En -f vcl-processor-1.sed vcl-example.vcl
// VCL.SHOW 0 1578 file with 3 spaces.vcl
// VCL.SHOW 0 1578 file.vcl
// VCL.SHOW 0 1578 file with TWOspaces.vcl
# шаг второй, вывести только имя файла
# используя команду “substitute”, с группами внутри регулярных выражений, отображается только нужная группa
# и это делается только для совпадений, ранее описанного поиска
> cat vcl-processor-2.sed
#// VCL.SHOW# {
s#.* [0-9]+ [0-9]+ (.*)$#1#
p
}
> sed -En -f vcl-processor-2.sed vcl-example.vcl
file with 3 spaces.vcl
file.vcl
file with TWOspaces.vcl
# шаг третий, получить только первый из результатов
# как и в случае с awk, добавляется немедленное завершения после печати первого найденного совпадения
> cat vcl-processor-3.sed
#// VCL.SHOW# {
s#.* [0-9]+ [0-9]+ (.*)$#1#
p
q
}
> sed -En -f vcl-processor-3.sed vcl-example.vcl
file with 3 spaces.vcl
# шаг четвертый, схлопнуть всё в однострочник, используя двоеточия для разделения команд
> sed -En -e '#// VCL.SHOW#{s#.* [0-9]+ [0-9]+ (.*)$#1#p;q;}' vcl-example.vcl
file with 3 spaces.vcl
Дакле, садржај скрипте за учитавање лака би изгледао отприлике овако:
VCL_FILE="$(echo "$VCL_SHOW" | sed -En '#// VCL.SHOW#{s#.*[0-9]+ [0-9]+ (.*)$#1#p;q;};')"
Горња логика се може сажети на следећи начин:
Ако стринг одговара регуларном изразу // VCL.SHOW
, затим похлепно прождире текст који укључује оба броја у том реду и сачувај све што је остало након ове операције. Издајте сачувану вредност и завршите програм.
Једноставно, зар не?
Били смо задовољни сед скриптом и чињеницом да она замењује сав оригинални код. Сви моји тестови су дали жељене резултате, тако да сам променио „учитавање лака“ на серверу и поново покренуо systemctl reload varnish
. Прљава грешка echo: write error: Broken pipe
поново нам се смејао у лице. Курсор који намигује чекао је да се унесе нова команда у мрачној празнини терминала...
Извор: ввв.хабр.цом