„Pažiūrėk“, – sako jis, rodydamas į vieną iš simbolių ekrane, – „Lažinuosi, mano raudona kepurė, kad jei pridėsime čia tai, ką ką tik tau atsiunčiau“, rodydamas į kitą kodo skyrių, „klaida nebebus. bus rodomas“.
Šiek tiek sutrikęs ir pavargęs pakeičiau sed išraišką, prie kurios kurį laiką dirbome, išsaugosiu failą ir paleidžiu systemctl varnish reload
. Klaidos pranešimas dingo...
„El. laiškai, kuriais apsikeičiau su kandidatu“, – tęsė kolega, o jo šypsena peraugo į tikrą džiaugsmo šypseną, – staiga supratau, kad tai lygiai ta pati problema!
Kaip viskas prasidėjo
Straipsnyje daroma prielaida, kad suprantama, kaip veikia bash, awk, sed ir systemd. Žinios apie laką pageidautinos, bet neprivalomos.
Fragmentų laiko žymos buvo pakeistos.
Parašyta su
Šis tekstas yra prieš dvi savaites anglų kalba išleisto originalo vertimas; vertimas
Dar vieną šiltą rudens rytą pro panoraminius langus šviečia saulė, nuo klaviatūros ilsisi šviežiai paruošto kofeino prisotinto gėrimo puodelis, ausinėse skamba mėgstama garsų simfonija, užgniaužusi mechaninių klaviatūrų ošimą, ir pirmasis įėjimas. atsilikusių bilietų sąraše „Kanban“ lentoje žaismingai švyti lemtingu pavadinimu „Investigate lakonreload“ sh: echo: I/O error in stage“ (Instaging „varnishreload sh: echo: I/O error“ instaging). Kalbant apie laką, klaidoms vietos yra ir negali būti, net jei jos nesukelia jokių problemų, kaip šiuo atveju.
Tiems, kurie nėra susipažinę su
Kaip rodo bilieto pavadinimas, klaida įvyko viename iš scenos serverių, o kadangi buvau tikras, kad lako išdėstymas scenoje veikia tinkamai, maniau, kad tai bus nedidelė klaida. Taigi, tik pranešimas, kuris atsidūrė jau uždarytame išvesties sraute. Pasiimu bilietą sau, visiškai pasitikėdamas, kad per mažiau nei 30 minučių pažymėsiu jį paruoštą, paglostysiu sau per nugarą, kad nuvaliau lentą nuo dar vienos šiukšlės ir grįšiu prie svarbesnių reikalų.
200 km/val. greičiu atsitrenkė į sieną
Failo atidarymas varnishreload
, viename iš serverių, kuriuose veikia Debian Stretch, pamačiau trumpesnį nei 200 eilučių apvalkalo scenarijų.
Peržiūrėjęs scenarijų, nepastebėjau nieko, kas galėtų sukelti problemų, jei kelis kartus paleisčiau jį tiesiai iš terminalo.
Juk tai etapas, jei ir nutrūks, niekas nesiskųs, na... ne per daug. Paleidžiu scenarijų ir žiūriu, kas bus įrašyta į terminalą, bet klaidų nebesimato.
Dar pora paleidimų, kad įsitikinčiau, jog negaliu atkurti klaidos be jokių papildomų pastangų, ir pradedu sugalvoti, kaip pakeisti šį scenarijų ir priversti jį vis tiek rodyti klaidą.
Ar scenarijus gali nepaisyti STDOUT (naudojant > &-
)? Arba STDERR? Nė vienas iš jų galiausiai nepasiteisino.
Matyt, sistema kažkaip pakeičia paleidimo aplinką, bet kaip ir kodėl?
Atidarau vim ir redaguoju varnishreload
, pridedant set -x
tiesiai po shebang, tikėdamiesi, kad scenarijaus derinimo išvestis parodys šiek tiek šviesos.
Failas ištaisytas, tai perkraunu laką ir matau, kad pakeitimas visiškai viską sulaužė... Išmetimas yra visiška netvarka, kurioje yra tonos C tipo kodo. Net slinkimo terminale neužtenka norint rasti, kur jis prasideda. Aš visiškai sutrikęs. Ar derinimo režimas gali paveikti programų, paleistų pagal scenarijų, veikimą? Ne, tai nesąmonė. Klaida kiaute? Mano galvoje tarsi tarakonai į skirtingas puses laksto keli galimi scenarijai. Gėrimo su kofeinu puodelis akimirksniu ištuštinamas, greita kelionė į virtuvę pasipildyti atsargų ir... važiuojame. Atidarau scenarijų ir atidžiau pažvelgiu į „shebang“: #!/bin/sh
.
/bin/sh
- tai tik nuoroda į bash, todėl scenarijus interpretuojamas su POSIX suderinamu režimu, tiesa? Ne taip! Numatytasis „Debian“ apvalkalas yra „dash“ ir būtent taip jis atrodo. /bin/sh
.
# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 24 2017 /bin/sh -> dash
Išbandymui pakeičiau shebangą į #!/bin/bash
, ištrinta set -x
ir pabandė dar kartą. Galiausiai, vėliau perkraunant laką, išvestyje pasirodė toleruotina klaida:
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 eilutė, štai!
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 }
Tačiau, kaip paaiškėjo, 124 eilutė yra gana tuščia ir neįdomi. Galėjau tik manyti, kad klaida įvyko kaip kelių eilučių eilutės, prasidedančios 116 eilutėje, dalis.
Kas galiausiai įrašoma į kintamąjį? VCL_FILE
dėl to, kad buvo vykdomas aukščiau pateiktas sub-apvalkalas?
Pradžioje jis siunčia kintamojo turinį VLC_SHOW
, sukurta 115 eilutėje, vykdant komandą per vamzdį. Ir kas tada ten vyksta?
Pirma, jis ten naudojamas varnishadm
, kuris yra lako montavimo paketo dalis, skirtas lako nustatymui neperkraunant.
Pogrupis vcl.show -v
naudojamas visai VCL konfigūracijai, nurodytai išvesti ${VCL_NAME}
, į STDOUT.
Norėdami parodyti dabartinę aktyvią VCL konfigūraciją, taip pat keletą ankstesnių lako nukreipimo konfigūracijų versijų, kurios vis dar yra atmintyje, galite naudoti komandą varnishadm vcl.list
, kurio išvestis bus panaši į toliau pateiktą:
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
Kintamoji vertė ${VCL_NAME}
yra įdiegta kitoje scenarijaus dalyje varnishreload
į šiuo metu aktyvios VCL pavadinimą, jei toks yra. Šiuo atveju tai bus „perkrauti_20190101_120000_12397“.
Puikus, kintamas ${VCL_SHOW}
yra visa lako konfigūracija, kol kas aišku. Dabar aš pagaliau suprantu, kodėl yra brūkšnelių išvestis set -x
pasirodė toks sugedęs – jame buvo gautos konfigūracijos turinys.
Svarbu suprasti, kad visa VCL konfigūracija dažnai gali būti sujungta iš kelių failų. C stiliaus komentarai naudojami norint nustatyti, kur tam tikri konfigūracijos failai buvo įtraukti į kitus, ir apie tai kalba toliau pateiktoje kodo fragmento eilutėje.
Komentarų, apibūdinančių įtrauktus failus, sintaksė yra tokio formato:
// VCL.SHOW <NUM> <NUM> <FILENAME>
Skaičiai šiame kontekste nėra svarbūs, mus domina failo pavadinimas.
Kas galiausiai nutinka komandų liūne, pradedant nuo 116 eilutės?
Leiskite face it.
Komanda susideda iš keturių dalių:
- Paprasta
echo
, kuris išspausdina kintamojo reikšmę${VCL_SHOW}
echo "$VCL_SHOW"
awk
, kuri ieško eilutės (įrašo), kur pirmas laukas, sulaužius tekstą, yra „//“, o antrasis – „VCL.SHOW“.
„Awk“ išrašys pirmąją eilutę, atitinkančią šiuos šablonus, ir nedelsdama sustabdys apdorojimą.awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
- Kodo blokas, kuriame saugomos lauko reikšmės į penkis kintamuosius, atskirtus tarpais. Penktasis FILE kintamasis gauna likusią eilutės dalį. Galiausiai paskutinis aidas išrašo kintamojo turinį
${FILE}
.{ read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
- Kadangi visi žingsniai nuo 1 iki 3 yra įtraukti į posluoksnį, išvedama reikšmė
$FILE
bus įrašytas į kintamąjįVCL_FILE
.
Kaip rodo komentaras 119 eilutėje, tai yra vienintelis tikslas – patikimai tvarkyti atvejus, kai VCL nurodys failus, kurių pavadinimuose yra tarpų.
Aš pakomentavau pradinę apdorojimo logiką ${VCL_FILE}
ir bandė pakeisti komandų seką, bet tai nieko neprivedė. Pas mane viskas veikė gerai, bet kai pradėjau servisą davė klaidą.
Panašu, kad rankiniu būdu paleidžiant scenarijų klaida tiesiog neatkuriama, o tariamas 30 minučių jau šešis kartus praėjo ir, be to, atsirado aukštesnio prioriteto užduotis, nustumianti į šalį kitus reikalus. Likusią savaitės dalį užpildė įvairios užduotys, o ją tik šiek tiek sumažino ataskaita apie sed ir pokalbis su kandidatu. Problema su klaida varnishreload
buvo negrįžtamai pasiklydęs laiko smėlyje.
Jūsų vadinamasis sed-fu... iš tikrųjų... šiukšlės
Kitą savaitę turėjau vieną gana laisvą dieną, todėl nusprendžiau dar kartą imtis šio bilieto. Tikėjausi, kad mano smegenyse kažkoks foninis procesas visą tą laiką ieškojo šios problemos sprendimo, ir šį kartą tikrai suprasiu, kas vyksta.
Kadangi praeitą kartą tiesiog pakeitus kodą nepadėjo, nusprendžiau jį perrašyti nuo 116 eilutės. Bet kokiu atveju esamas kodas buvo kvailas. Ir jo naudoti visiškai nereikia read
.
Dar kartą pažiūrėjus į klaidą:
sh: echo: broken pipe
— aidas pasirodo dviejose šios komandos vietose, bet įtariu, kad pirmasis yra labiau tikėtinas kaltininkas (ar bent jau bendrininkas). Awk taip pat nekelia pasitikėjimo. Ir tuo atveju, jei tai tikrai yra awk | {read; echo}
dizainas sukelia visas šias problemas, kodėl jo nepakeitus? Ši vienos eilutės komanda nenaudoja visų awk funkcijų ir net šios papildomos read
papildomai.
Nuo praėjusios savaitės buvo pranešta apie sed
, norėjau išbandyti naujai įgytus įgūdžius ir supaprastinti echo | awk | { read; echo}
į labiau suprantamą echo | sed
. Nors tai tikrai nėra geriausias būdas nustatyti klaidą, maniau, kad bent jau išbandysiu savo sed-fu ir galbūt sužinosiu ką nors naujo apie problemą. Pakeliui paprašiau kolegos, sed pokalbio autoriaus, kad padėtų man sugalvoti efektyvesnį sed scenarijų.
Aš numečiau turinį varnishadm vcl.show -v "$VCL_NAME"
į failą, kad galėčiau sutelkti dėmesį į sed scenarijaus rašymą be jokio vargo perkraunant paslaugą.
Trumpas aprašymas, kaip tiksliai galima rasti sed procesų įvestį n
aiškiai nurodytas kaip eilučių skyriklis.
Keliais praėjimais ir remdamiesi mano kolegos rekomendacijomis, parašėme sed scenarijų, kuris davė tokį patį rezultatą kaip ir visa originali 116 eilutė.
Žemiau yra pavyzdinis failas su įvesties duomenimis:
> 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
Galbūt tai nėra akivaizdu iš aukščiau pateikto aprašymo, bet mus domina tik pirmasis komentaras // VCL.SHOW
, o įvesties duomenyse jų gali būti keletas. Štai kodėl originalus awk baigiasi po pirmojo mačo.
# шаг первый, вывести только строки с комментариями
# используя возможности 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
Taigi, lako atnaujinimo scenarijaus turinys atrodys maždaug taip:
VCL_FILE="$(echo "$VCL_SHOW" | sed -En '#// VCL.SHOW#{s#.*[0-9]+ [0-9]+ (.*)$#1#p;q;};')"
Aukščiau pateiktą logiką galima trumpai išreikšti taip:
Jei eilutė atitinka reguliariąją išraišką // VCL.SHOW
, tada godžiai surykite tekstą, kuriame yra abu šios eilutės skaičiai, ir išsaugokite viską, kas liko po šios operacijos. Išleiskite išsaugotą vertę ir užbaikite programą.
Paprasta, ar ne?
Buvome patenkinti sed scenarijumi ir tuo, kad jis pakeitė visą pradinį kodą. Visi mano testai davė norimus rezultatus, todėl pakeičiau „lako įkėlimą“ serveryje ir paleidau dar kartą systemctl reload varnish
. Bloga klaida echo: write error: Broken pipe
vėl nusijuokė mums į veidus. Mirksintis žymeklis laukė, kol tamsioje terminalo tuštumoje bus įvesta nauja komanda...
Šaltinis: www.habr.com