, vajutades nuppe viimase 20 minuti jooksul nagu oleks see tema elu otsustav hetk, pöördub ta minu poole pool-karmika väljendiga silmades ja salapärase naeratusega — "Kutt, mul on tunne, et ma sain aru."
"Vaata siia," ütleb ta, osutades ühelt ekraanil olevalt sümbolilt — "Lähme vedamiseks minu punase mütsi peale, et kui me lisame siia selle, mis ma just saadetud, " — osutades teisele koodialale — "siis viga enam ei ilmu."
Veidi segaduses ja väsinuna muudan sed väljendit, millega oleme juba mõnda aega töötanud, salvestan faili ja käivitame systemctl varnish reload. Viga on kadunud...
"Meil, mida ma kandidaadiga vahetasin," jätkas mu kolleeg, samal ajal kui ta naeratus muutus ehtsaks rõõmuks, "äkki sain aru, et see on sama probleem!"
Kust kõik algas
Artikkel eeldab bash, awk, sed ja systemd tööpõhimõtete tundmist. Varnishi teadmine on teretulnud, kuid ei ole kohustuslik.
Ajastuse tempod on võetud väljaminekuks.
Kirjutatud koos .
See tekst on tõlge originaalist, mis ilmus inglise keeles kaks nädalat tagasi; tõlge .
Päike paistab läbi panoraakaknad mõnel sooja sügisese hommikuajal, tass värskelt valmistatud kofeiiniga jooki on klaviatuuri kõrvale asetatud, kõrvaklappides mängib lemmik sümfooniline helide kombinatsioon, varjutades mehhaaniliste klaviatuuride kriiksumise, ja esimesena kanban-plaadi backlogis helendab määratletud pealkiri "Uuri välja varnishreload sh: echo: I/O error stage'is". Kui jutt tuleb varnishist, ei ole vigadele kohta, isegi kui need ei ilmne mingite probleemidena nagu käesoleval juhul.
Neile, kes ei tunne , see on lihtne shell skript, mida kasutatakse konfiguratsiooni uuendamiseks — tuntud ka kui VCL.
Nagu piletinimi viitab, tekkis viga ühel serveritest stendil ja kuna olin kindel, et varnishi marsruutimine stendil töötab korralikult, oletasin, et see on lihtsalt väike viga. See on lihtsalt sõnum, mis sattus juba suletud väljundvoogu. Võtan pileti endale, olles täiesti kindel, et märgin selle vähem kui 30 minutiga valmis, kiidan end selja patsutamisega selle eest, et puhastasin tahvlit uuest prahtist ja naasen tähtsamate asjade juurde.
Seina sisse sõites kiirusel 200 km/h
Faili avades varnishreload, ühel Debian Stretchi haldusel serveritest, märkasin vähem kui 200 rea pikkust shelli skripti.
Skripti läbi vaadates ei märganud ma midagi sellist, mis võiks põhjustada probleeme selle korduvatel käivitamisel otse terminalist.
Lõppude lõpuks on see stend, isegi kui see peaks katki minema, ei peaks keegi liiga palju kaebama. Käivitan skripti ja vaatan, mis terminalis välja visatakse, kuid vigu enam näha ei ole.
Veel paar käivitust, et veenduda, et ma ei saa viga korrata ilma mingite lisategevusteta, ja hakkasin mõtlema, kuidas seda skripti muuta, et see ikkagi vea kuvama paneks.
Kas peaksin skripti STDOUT-i suunama (kasutades > &-)? Või STDERR? Ei, kumbki ei töötanud.
Ilmselt muudab systemd mingil moel käitamise keskkonda, aga kuidas ja, miks?
Lülitan sisse vim'i ja redigeerin varnishreload, lisades set -x otse shebang'i alla, lootes, et skripti debugeerimise väljund heidab veidi valgust.
Fail on parandatud, nii et taaskäivitame varnishi ja näeme, et muudatus hävitas kogu asja… Väljund on totaalselt segadus, kus on tonne C-sarnast koodi. Isegi terminalis kerimine ei piisa, et leida, kus see algab. Olen täielikus segaduses. Kas silumise režiim võib mõjutada skriptis käivitatavate programmide tööd? Ei, see on vale. Vead shellis? Mitmed võimalikud stsenaariumid jooksevad mu peas ringi nagu koolibrid eri suundades. Tass kofeiini täis jooki tühjeneb hetkega, kiire reis kööki, et varusid täiendada ja... alustame. Avan skripti ja vaatan shebang'i tähelepanelikult: #!/bin/sh.
/bin/sh — see on lihtsalt sümbolsid bashile, nii et skript tõlgendatakse POSIX-ühilduvas režiimis, eks? Ei ole nii! Distro vaikimisi shell Debiani süsteemis on dash ja täpselt selleks, /bin/sh.
# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 24 2017 /bin/sh -> dashKatse nimel muutsin shebang'i #!/bin/bash, eemaldasin set -x ja proovisin veel kord. Lõ finally, järgmise Varnish'i taaskäivitamise ajal ilmus väljundisse arvestatav viga:
Jan 01 12:00:00 hostname varnishreload[32604]: /usr/sbin/varnishreload: rida 124: echo: kirjutamisviga: katkenud toru
Jan 01 12:00:00 hostname varnishreload[32604]: VCL 'reload_20190101_120000_32604' kompileeritudRida 124, siin see on!
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 # kõik see tseremoniaal, et hallata tühikuid FILE-is
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 "VCL faili nime saamine ebaõnnestus"
129 fi
130
131 echo "$VCL_FILE"
132 }Kuid nagu selgus, on rida 124 üsna tühi ja huvi ei paku. Ma sain vaid oletada, et viga tekkis osana mitmerealisest, mis algab 116. real.
Mis lõpuks salvestatakse muutuja VCL_FILE tulemusena eespool nimetatud alamshelli täitmisest?
Alustuseks saadab see muutuja sisu VLC_SHOW, mis loodi 115. real, järgmisele käsule läbi toru. Ja seal, mis siis toimub?
Esiteks kasutatakse seal varnishadm, mis on osa Varnishi installipaketist, Varnishi seadistamiseks ilma taaskäivitamiseta.
Alamsüsteem vcl.show -v kasutatakse kogu VCL-i konfiguratsiooni väljundamiseks, mis on määratud ${VCL_NAME}, STDOUT-is.
Käesoleva aktiivse VCL konfiguratsiooni kuvamiseks, samuti mõnede varasemate Varnishi marsruudistamise konfiguratsioonide versioonide kuvamiseks, mis on endiselt mälus, võite kasutada käsku varnishadm vcl.list, mille väljund on sarnane allolevale:
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_12587Muu muutuja väärtus ${VCL_NAME} seatakse skripti teises osas varnishreload aktiivseks hetkel VCL-iks, kui see olemas on. Antud juhul on see “reload_20190101_120000_12397”.
Suurepärane, muutuja ${VCL_SHOW} sisaldab kogu Varnishi konfiguratsiooni, nüüd on selge. Nüüd lõpuks mõtlesin, miks dash'i väljund set -x oli selline katki — see sisaldas tulemuseks saanud konfiguratsiooni sisu.
Oluline on mõista, et VCL-i täielik konfiguratsioon võib sageli koosneda mitmest failist. C-stiilis kommentaare kasutatakse määramaks, kus ühe konfiguratsioonifaili sisu on teise sisse lisatud, ja just sellest räägib ka allpool toodud koodijupi rida.
Kaasa sisaldavate failide kommentaaride süntaks on järgmine:
// VCL.SHOW <NUM> <NUM> <FILENAME>Numbrid selle konteksti juures ei ole olulised, meid huvitab faili nimi.
Mis toimub käskude soontes, mis algavad real 116?
Vaatame lähemalt.
Komand koosneb neljast osast:
- Lihtne
echo, mis väljastab muutuja väärtuse${VCL_SHOW}echo "$VCL_SHOW" awk, mis otsib rida (kirjet), kus esimene väli tekstist jagamisel on "//" ja teine on "VCL.SHOW".
Awk väljastab esimese rea, mis vastab nendele mustritele, ja seejärel lõpetab töötlemise koheselt.awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'- Koodiblokk, mis salvestab viies muutuja väärtused, mis on jagatud tühikutega. Viies muutuja FILE saab rea ülejäänud osa. Lõpuks väljastab viimane echo muutuja sisu
${FILE}.{ read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" } - Kuna kõik sammud 1 kuni 3 on kirjutatud sub-shelli, siis muutuja
$FILEväärtus salvestatakse muutujaVCL_FILE.
Nagu 119. rea kommentaarist järeldub, teenib see ainukest eesmärki: usaldusväärselt töödelda juhtumeid, kus VCL viitab failidele, mille nimedes on tühikute sümboleid.
Ma kommenteerisin algse töötluse loogika jaoks ${VCL_FILE} ja proovisin käskude järjekorda muuta, kuid see ei viinud kuhugi. Mul toimis kõik kenasti, ja teenuse käivitamisel tekkis tõrge.
Tundub, et viga ei esine käsitsi skripti käivitamisel, samas kui eeldatav 30-minutiline aeg on juba kuus korda möödunud, ja lisaks sellele on tekkinud prioriteetsem ülesanne, mis lükkas muud tegevused kõrvale. Ülejäänud nädal oli täis erinevaid ülesandeid, mõnevõrra leevendatud sed-st ja kandidaadiga intervjuust. Tõrke probleem varnishreload oli ajas pöördumatult kadunud.
Teie nii öeldud sed-fu... tegelikult... on jama.
Järgmine nädal oli mul üks üsna vaba päev, seega otsustasin uuesti selle tiketi kallal tegutseda. Lootsin, et mu ajus on mingi taustaprotsess kogu selle aja lahendust otsinud ja seekord saan ma kindlasti aru, mis asi see on.
Kuna eelmisel korral lihtne koodi muutmine ei aidanud, otsustasin selle lihtsalt 116. realt uuesti kirjutada. Igatahes oli olemasolev kood tõeliselt kehva kvaliteediga. Ja seal pole mingit vajadust kasutada lugema.
Vaadates viga veel kord:
sh: echo: katkine toru — selles käsus on echo kahes kohas, kuid ma kahtlustan, et esimene on tõenäolisem süüdlane (noh, või vähemalt kaasosalüss). Awk ei inspireeri ka usaldust. Ja juhul, kui tõepoolest see awk | {read; echo} struktuur põhjustab kõik need probleemid, miks seda mitte asendada? See üherealine käsk ei kasuta kõiki awk võimalusi, pluss see on lisaks lugema üheks lisaks.
Kuna eelmisel nädalal oli ettekande teema sed, tahtsin proovida oma hiljuti omandatud oskusi ja lihtsustasin echo | awk | { read; echo} mõistetavamaks echo | sed. Kuigi see ei ole kindlasti parim lähenemine vea tuvastamiseks, arvasin, et vähemalt proovin oma sed-fu ja võib-olla õpin midagi uut probleemi kohta. Teel küsisin oma kolleegilt, kes on sed'i ettekande autor, abi, et aidata mul välja mõelda efektiivsem sed skript.
Saatsin sisu varnishadm vcl.show -v "$VCL_NAME" faili, nii et saaksin keskenduda sed skripti kirjutamisele ilma muredega teenuse taaskäivitamisest.
Lühikese ülevaate selle kohta, kuidas sed täpselt sisendit töötleb, leiab . Sed'i lähtekoodis on märk n selgelt määratletud ridade eraldajana.
Mitme korduse ja oma kolleegi soovituste abil kirjutasime sed skripti, mis andis sama tulemuse, mis kogu algne rida 116.
Allpool on näidisfail sisendandmetega:
> cat vcl-example.vcl
Text
// VCL.SHOW 0 1578 fail, kus on 3 tühikut.vcl
Rohkem teksti
// VCL.SHOW 0 1578 fail.vcl
Veel rohkem teksti
// VCL.SHOW 0 1578 fail, kus on KAKStühikut.vcl
Lõplik tekstSee ei pruugi olla selge ülaltoodud kirjelduse põhjal, kuid meid huvitab ainult esimene kommentaar // VCL.SHOW, ja sisendis võib olla mitu. Just seetõttu lõpetab originaal awk oma töö pärast esimest vastet.
# шаг первый, вывести только строки с комментариями
# используя возможности 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.vclNii et skripti varnishreload sisu näeb välja umbes selline:
VCL_FILE="$(echo "$VCL_SHOW" | sed -En '#// VCL.SHOW#{s#.*[0-9]+ [0-9]+ (.*)$#1#p;q;};')"Ülaltoodud loogikat saab lühidalt väljendada järgmisega:
Kui string vastab regulaaravaldistele // VCL.SHOW, siis ahne röövimise teel proovi tekst, mis sisaldab mõlemat numbrit selles stringis, ning salvesta kõik, mis pärast seda operatsiooni jääb. Anna salvestatud väärtus ja lõpeta programm.
Lihtne, eks?
Oli tore, et sed skript asendab kogu algset koodi. Kõik mu testid andsid soovitud tulemusi, seega muutsin "varnishreload" serveris ja käivitasin uuesti systemctl reload varnish. Nefaste viga echo: kirjutamisviga: katkenud toru naeris taas meie näo üle. Vilkuv kursori märk ootas uusi käske tumedas tühjuses terminalis...
Allikas: habr.com
