"Katso tänne", hän sanoo ja osoittaa yhtä näytöllä olevista symboleista. "Luon vetoa, että jos lisäämme sen, mitä juuri lähetin sinulle tänne", osoittaa toista koodin osaa, "virhettä ei enää näytetä."
Hieman hämmentyneenä ja väsyneenä vaihdan sed-lausetta, jota olemme työstäneet jonkin aikaa, tallennan tiedoston ja suoritan systemctl varnish reload
. Virheilmoitus on kadonnut...
"Sähköpostit, joita vaihdoin ehdokkaan kanssa", kollegani jatkoi, kun hänen virnistyksensä muuttuu aidoksi hymyksi, joka on täynnä iloa, "Yhtäkkiä tajusin, että tämä on täsmälleen sama ongelma!"
Kuinka kaikki alkoi
Artikkelissa oletetaan ymmärtävän, kuinka bash, awk, sed ja systemd toimivat. Lakkatuntemus katsotaan eduksi, mutta ei pakollinen.
Katkelmien aikaleimat on muutettu.
Kirjoitettu kanssa
Tämä teksti on käännös alkuperäisestä, joka julkaistiin englanniksi kaksi viikkoa sitten; käännös
Aurinko paistaa panoraamaikkunoista toisena lämpimänä syysaamuna, kuppi tuoretta kofeiinipitoista juomaa lepää erossa koskettimistosta, suosikki äänten sinfonia soi mekaanisten koskettimien kahinaa kuulokkeissa, ja kanbantaulun backlog lippulistan ensimmäinen merkintä hehkuu leikkisästi kohtalokkaalla otsikolla "Investigate sh:Ovarnegate nishreload sh : echo: I/O error" vaiheessa). Lakkauksessa ei ole eikä voi olla virheitä, vaikka ne eivät aiheuta ongelmia, kuten tässä tapauksessa.
Niille, jotka eivät tunne
Kuten lipun otsikko viittaa, virhe tapahtui yhdellä lavan palvelimista ja koska olin varma, että lakan reititys lavalla toimi oikein, oletin tämän olevan pieni virhe. Joten, vain viesti, joka joutui jo suljettuun lähtövirtaan. Otan lipun itselleni, täysin luottavaisin mielin, että merkitsen sen valmiiksi alle 30 minuutissa, taputan itseäni olkapäälle seuraavan roskalaudan poistamiseksi ja palaan tärkeämpiin asioihin.
Törmäsi seinään nopeudella 200 km/h
Tiedoston avaaminen varnishreload
, yhdellä Debian Stretchiä käyttävistä palvelimista näin alle 200 rivin pituisen komentotulkkikomentosarjan.
Suorittaessani komentosarjaa en nähnyt mitään, mikä voisi aiheuttaa ongelmia, kun sitä suoritettiin useita kertoja suoraan päätteestä.
Loppujen lopuksi tämä on vaihe, vaikka se rikkoutuisi, kukaan ei valita, no ... ei liikaa. Suoritan skriptin ja katson, mitä päätelaitteelle kirjoitetaan, mutta virheet eivät enää näy.
Vielä pari kertaa varmistaakseni, etten voi toistaa virhettä ilman ylimääräistä vaivaa, ja alan miettiä, kuinka muuttaa tätä komentosarjaa ja saada se silti aiheuttamaan virheen.
Voiko komentosarja estää STDOUT (käyttäen > &-
)? Tai STDERR? Kumpikaan ei toiminut lopulta.
Ilmeisesti systemd muuttaa ajoympäristöä jollain tavalla, mutta miten ja miksi?
Laitan vimin päälle ja muokkaan varnishreload
, lisäämällä set -x
aivan shebangin alla, toivoen, että käsikirjoituksen tulosten virheenkorjaus valaisee hieman.
Tiedosto on korjattu, joten lataan lakan uudelleen ja näen, että muutos rikkoi kaiken... Pakoputki on täydellinen sotku, jossa on tonnia C-kaltaista koodia. Edes terminaalin vierittäminen ei riitä löytämään, mistä se alkaa. Olen täysin hämmentynyt. Voiko debug-tila vaikuttaa komentosarjassa suoritettavien ohjelmien toimintaan? Ei hevonpaskaa. Bugi kuoressa? Päässäni lentää useita mahdollisia skenaarioita kuin torakoita eri suuntiin. Kupillinen kofeiinia täynnä olevaa juomaa tyhjenee heti, pikamatka keittiöön lisäravintoa varten ja… mennään. Avaan käsikirjoituksen ja katson tarkemmin shebangia: #!/bin/sh
.
/bin/sh
- Tämä on vain bash-symlink, joten skripti tulkitaan POSIX-yhteensopivassa tilassa, eikö niin? Se ei ollut siellä! Debianin oletuskuori on dash, mikä on juuri sitä /bin/sh
.
# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 24 2017 /bin/sh -> dash
Kokeilun vuoksi vaihdoin shebangin muotoon #!/bin/bash
, poistettu set -x
ja yritti uudelleen. Lopuksi myöhemmän lakan uudelleenlatauksen yhteydessä tulostukseen ilmestyi siedettävä virhe:
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
Linja 124, tässä se 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 # 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 }
Mutta kuten kävi ilmi, rivi 124 on melko tyhjä eikä kiinnosta. Voisin vain olettaa, että virhe tapahtui osana riviltä 116 alkavaa monirivistä.
Mitä muuttujaan lopulta kirjoitetaan VCL_FILE
yllä olevan alikulkon suorittamisen seurauksena?
Alussa se lähettää muuttujan sisällön VLC_SHOW
, luotu rivillä 115, seuraavaan komentoon putken kautta. Ja mitä siellä sitten tapahtuu?
Ensinnäkin se käyttää varnishadm
, joka on osa lakan asennuspakettia, jotta voit määrittää lakan ilman uudelleenkäynnistystä.
alakomento vcl.show -v
käytetään koko kohdassa määritellyn VCL-kokoonpanon tulostamiseen ${VCL_NAME}
, STDOUT.
Voit näyttää tällä hetkellä aktiivisen VCL-kokoonpanon sekä useita aiempia versioita lakan reitityskokoonpanoista, jotka ovat vielä muistissa, käyttämällä komentoa varnishadm vcl.list
, jonka tulos on seuraavanlainen:
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
Muuttuva arvo ${VCL_NAME}
asetettu käsikirjoituksen toisessa osassa varnishreload
tällä hetkellä aktiivisen VCL:n nimeen, jos sellainen on. Tässä tapauksessa se on "reload_20190101_120000_12397".
Okei, vaihteleva. ${VCL_SHOW}
sisältää täydellisen lakan kokoonpanon, toistaiseksi selkeä. Nyt olen vihdoin ymmärtänyt, miksi dash-tulostus set -x
osoittautui niin rikki - se sisälsi tuloksena olevan kokoonpanon sisällön.
On tärkeää ymmärtää, että täydellinen VCL-kokoonpano voidaan usein koota yhteen useista tiedostoista. C-tyylisiä kommentteja käytetään määrittämään, missä yksi asetustiedosto on sisällytetty toiseen, ja juuri siitä seuraavassa koodinpätkärivissä on kyse.
Mukana olevia tiedostoja kuvaavien kommenttien syntaksi on seuraavassa muodossa:
// VCL.SHOW <NUM> <NUM> <FILENAME>
Numerot eivät tässä yhteydessä ole tärkeitä, olemme kiinnostuneita tiedoston nimestä.
Mitä sitten tapahtuu komentojen suossa, joka alkaa riviltä 116?
Totta puhuen.
Komento koostuu neljästä osasta:
- yksinkertainen
echo
, joka näyttää muuttujan arvon${VCL_SHOW}
echo "$VCL_SHOW"
awk
, joka etsii riviä (tietuetta), jossa ensimmäinen kenttä tekstin jakamisen jälkeen on "//" ja toinen on "VCL.SHOW".
Awk kirjoittaa ensimmäisen rivin, joka vastaa näitä kuvioita, ja lopettaa käsittelyn välittömästi.awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
- Koodilohko, joka tallentaa kentän arvot viiteen muuttujaan välilyönnillä erotettuna. Viides muuttuja FILE vastaanottaa loput rivistä. Lopuksi viimeinen kaiku kirjoittaa muuttujan sisällön
${FILE}
.{ read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
- Koska kaikki vaiheet 1 - 3 on suljettu alikuoreen, arvon tulos
$FILE
kirjoitetaan muuttujaanVCL_FILE
.
Kuten rivin 119 kommentti ehdottaa, tämä palvelee ainoaa tarkoitusta käsitellä luotettavasti tapauksia, joissa VCL viittaa tiedostoihin, joiden nimissä on välilyöntejä.
Olen kommentoinut alkuperäistä käsittelylogiikkaa ${VCL_FILE}
ja yritti muuttaa komentojen järjestystä, mutta se ei johtanut mihinkään. Kaikki toimi minulle siististi, ja palvelun käynnistämisen tapauksessa se antoi virheen.
Näyttää siltä, että virhe ei yksinkertaisesti ole toistettavissa käsin skriptiä ajettaessa, kun taas arvioidut 30 minuuttia ovat päättyneet jo kuusi kertaa ja lisäksi on ilmaantunut korkeamman prioriteetin tehtävä, joka työntää muut tapaukset sivuun. Loppuviikko oli täynnä erilaisia tehtäviä, ja sitä laimensi vain hieman sed-keskustelu ja haastattelu ehdokkaan kanssa. Virhe ongelma sisään varnishreload
peruuttamattomasti eksynyt ajan hiekkaan.
Sinun niin kutsuttu sed-fu... itse asiassa... roskaa
Seuraavalla viikolla oli yksi melko vapaa päivä, joten päätin ostaa tämän lipun uudelleen. Toivoin, että aivoissani jokin taustaprosessi koko tämän ajan etsi ratkaisua tähän ongelmaan, ja tällä kertaa ymmärrän varmasti, mikä on vialla.
Koska viime kerralla pelkkä koodin vaihtaminen ei auttanut, päätin vain kirjoittaa sen uudelleen 116. riviltä alkaen. Joka tapauksessa nykyinen koodi oli typerä. Eikä ole mitään tarvetta käyttää read
.
Katsotaanpa virhettä uudelleen:
sh: echo: broken pipe
- tässä komennossa kaiku on kahdessa paikassa, mutta epäilen, että ensimmäinen on todennäköisempi syyllinen (no, tai ainakin rikoskumppani). Awk ei myöskään herätä luottamusta. Ja jos se todella on awk | {read; echo}
suunnittelu johtaa kaikkiin näihin ongelmiin, miksi ei korvata sitä? Tämä yksirivinen komento ei käytä kaikkia awkin ominaisuuksia eikä edes tätä ylimääräistä read
lisäyksessä.
Viime viikosta lähtien on ollut raporttia sed
Halusin kokeilla äskettäin hankittuja taitojani ja yksinkertaistaa echo | awk | { read; echo}
ymmärrettävämpään suuntaan echo | sed
. Vaikka tämä ei todellakaan ole paras tapa saada vikaa kiinni, ajattelin ainakin kokeilla sed-fuani ja ehkä oppia jotain uutta ongelmasta. Pyysin matkan varrella kollegaani, sed talk -kirjoittajaa, auttamaan minua löytämään tehokkaampi sed-käsikirjoitus.
Pudotin sisällön varnishadm vcl.show -v "$VCL_NAME"
tiedostoon, jotta voin keskittyä sed-skriptin kirjoittamiseen ilman palvelun uudelleenkäynnistymisen vaivaa.
Lyhyt kuvaus siitä, kuinka sed käsittelee syötettä, löytyy kohdasta n
nimenomaisesti määritelty rivierottimeksi.
Kirjoitimme useissa kierroksissa ja kollegani neuvoista sed-käsikirjoituksen, joka antoi saman tuloksen kuin koko alkuperäinen rivi 116.
Alla on esimerkkitiedosto syötetiedoilla:
> 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
Se ei ehkä ole selvää yllä olevasta kuvauksesta, mutta olemme kiinnostuneita vain ensimmäisestä kommentista // VCL.SHOW
, ja niitä voi olla useita syöttötiedoissa. Tästä syystä alkuperäinen awk päättyy ensimmäisen ottelun jälkeen.
# шаг первый, вывести только строки с комментариями
# используя возможности 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
Joten varnishreload-skriptin sisältö näyttäisi suunnilleen tältä:
VCL_FILE="$(echo "$VCL_SHOW" | sed -En '#// VCL.SHOW#{s#.*[0-9]+ [0-9]+ (.*)$#1#p;q;};')"
Yllä oleva logiikka voidaan tiivistää seuraavasti:
Jos merkkijono vastaa säännöllistä lauseketta // VCL.SHOW
, niele sitten ahneesti teksti, joka sisältää molemmat numerot kyseisellä rivillä, ja tallenna kaikki tämän toiminnon jälkeen jäljellä olevat. Anna tallennettu arvo ja lopeta ohjelma.
Yksinkertaista, eikö?
Olimme tyytyväisiä sed-skriptiin ja siihen, että se korvaa kaiken alkuperäisen koodin. Kaikki testini antoivat halutut tulokset, joten vaihdoin "lakan uudelleenlatauksen" palvelimella ja juoksin uudelleen systemctl reload varnish
. Likainen virhe echo: write error: Broken pipe
nauroi taas päin naamaa. Silmäilevä kohdistin odotti uutta komentoa terminaalin pimeässä tyhjiössä...
Lähde: will.com