"Sjoch hjir," seit er, en wiist nei ien fan 'e symboalen op it skerm, "ik wedzje op myn reade hoed dat as wy hjir tafoegje wat ik jo krekt stjoerde," wiist nei in oare seksje fan koade, "de flater sil net mear wêze sil werjûn wurde."
In bytsje fernuvere en wurch, ik feroarje de sed-ekspresje wêr't wy in skoft oan wurke hawwe, bewarje it bestân en rinne systemctl varnish reload
. It flaterberjocht is ferdwûn...
"De e-mails dy't ik útwiksele mei de kandidaat," gie myn kollega troch, doe't syn gnyske útgroeide ta in echte glimke fan freugde, "It kaam my ynienen troch dat dit krekt itselde probleem is!"
Hoe't it allegear begûn
It artikel giet út fan in begryp fan hoe't bash, awk, sed en systemd wurkje. Kennis fan lak is de foarkar, mar net fereaske.
Tiidstempels yn snippets binne feroare.
Skreaun mei
Dizze tekst is in oersetting fan it orizjineel dat twa wiken lyn yn it Ingelsk publisearre is; oersetting
De sinne skynt troch de panoramyske ruten op in oare waarme hjerstmoarn, in kopke farsk tariede kafee-rike drank rêst fuort fan it toetseboerd, jo favorite symfony fan lûden klinkt yn jo koptelefoan, ferdrinkt it ritseljen fan meganyske toetseboerden, en de earste yngong yn de list fan efterstân kaartsjes op de Kanban board boartlik gloeit mei de needlottige titel "Undersykje varnishreload" sh: echo: I / O flater yn staging" (Undersykje "varnishreload sh: echo: I / O flater" yn staging). As it giet om fernis is en kin der gjin romte wêze foar flaters, sels as se gjin problemen opleverje lykas yn dit gefal.
Foar dyjingen dy't net bekend mei
As de titel fan it kaartsje suggerearret, barde de flater op ien fan 'e tsjinners op it poadium, en om't ik wie der wis fan dat de lak routing op it poadium wurke goed, Ik oannommen dat dit soe wêze in lytse flater. Dus, gewoan in berjocht dat einige yn in al sletten útfierstream. Ik nim it kaartsje foar mysels, yn fol fertrouwen dat ik it yn minder as 30 minuten klear markearje sil, klopje mysels op 'e rêch foar it skjinmeitsjen fan it boerd fan noch in rommel en kom werom nei wichtiger saken.
Yn in muorre botst mei 200 km/h
It iepenjen fan de triem varnishreload
, Op ien fan 'e tsjinners dy't Debian Stretch draaie, seach ik in shellskript fan minder as 200 rigels lang.
Nei't ik it skript trochgien hie, haw ik neat opmurken dat kin resultearje yn problemen by it útfieren fan it meardere kearen direkt fan 'e terminal.
Dit is ommers in poadium, ek al brekt it, gjinien sil kleie, no... net te folle. Ik rinne it skript en sjoch wat sil wurde skreaun oan de terminal, mar de flaters binne net mear sichtber.
In pear mear rint om te soargjen dat ik kin net reprodusearje de flater sûnder ekstra ynspannings , en ik begjin út te finen hoe't te feroarjen dit skript en meitsje it noch smyt in flater.
Kin it skript STDOUT oerskriuwe (mei > &-
)? Of STDERR? Gjin fan beide wurke op it lêst.
Blykber feroaret systemd op ien of oare manier de opstartomjouwing, mar hoe, en wêrom?
Ik iepenje vim en bewurkje varnishreload
, tafoegjen set -x
rjochts ûnder de shebang, yn 'e hoop dat de debug-útfier fan it skript wat ljocht sil smyt.
De triem is korrizjearre, dus ik laad lak opnij en sjoch dat de feroaring alles folslein bruts ... De útlaat is in folsleine puinhoop, wêryn d'r tonnen C-like koade binne. Sels scrollen yn 'e terminal is net genôch om te finen wêr't it begjint. Ik bin hielendal yn de war. Kin debuggen modus ynfloed op de wurking fan programma's lansearre yn in skript? Nee, it is ûnsin. Bug in de shell? Ferskate mooglike senario's rinne troch myn holle as kakkerlakken yn ferskate rjochtingen. De beker fan 'e kafeïnefolle drank is daliks leech, in flugge reis nei de keuken om de foarried oan te foljen en ... we geane. Ik iepenje it skript en besjoch de shebang fan tichterby: #!/bin/sh
.
/bin/sh
- dit is gewoan in symlink nei bash, dus it skript wurdt ynterpretearre yn POSIX-kompatibele modus, toch? Net sa! De standert shell op Debian is dash, en dat is krekt wat it liket. /bin/sh
.
# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 24 2017 /bin/sh -> dash
As test feroare ik de shebang nei #!/bin/bash
, wiske set -x
en nochris besocht. Uteinlik, by it folgjende opnij opstarten fan lak, ferskynde in tolerabele flater yn 'e útfier:
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
Line 124, hjir is it!
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 }
Mar sa docht bliken, line 124 is frij leech en fan gjin belang. Ik koe allinne mar oannimme dat de flater barde as ûnderdiel fan in multiline string begjinnend op rigel 116.
Wat einiget skreaun nei de fariabele? VCL_FILE
as gefolch fan it útfieren fan de boppesteande sub-shell?
Oan it begjin stjoert it de ynhâld fan 'e fariabele VLC_SHOW
, makke op rigel 115, nei it kommando troch de piip. En wat bart dêr dan?
Earst wurdt it dêr brûkt varnishadm
, dy't diel útmakket fan it fernisynstallaasjepakket, foar it ynstellen fan lak sûnder opnij te begjinnen.
Sub-team vcl.show -v
brûkt om de folsleine VCL-konfiguraasje út te fieren spesifisearre yn ${VCL_NAME}
, to STDOUT.
Om de aktuele aktive VCL-konfiguraasje wer te jaan, lykas ferskate eardere ferzjes fan fernisroutingkonfiguraasjes dy't noch yn it ûnthâld binne, kinne jo it kommando brûke varnishadm vcl.list
, wêrfan de útfier fergelykber is mei de hjirûnder:
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
Fariabele wearde ${VCL_NAME}
is ynstallearre yn in oar diel fan it skript varnishreload
oan de namme fan de op it stuit aktive VCL, as der ien is. Yn dit gefal sil it "reload_20190101_120000_12397" wêze.
Geweldich, fariabel ${VCL_SHOW}
befettet folsleine konfiguraasje foar lak, dúdlik foar no. No begryp ik einliks wêrom't de dash-útfier is set -x
die bliken sa brutsen te wêzen - it omfette de ynhâld fan 'e resultearjende konfiguraasje.
It is wichtich om te begripen dat in folsleine VCL-konfiguraasje faaks kin wurde gearstald út ferskate bestannen. Opmerkings yn C-styl wurde brûkt om te identifisearjen wêr't bepaalde konfiguraasjebestannen binne opnommen yn oaren, en dat is wêr't de folgjende rigel fan koadefragment oer giet.
De syntaksis foar opmerkings dy't opnommen bestannen beskriuwe is yn it folgjende formaat:
// VCL.SHOW <NUM> <NUM> <FILENAME>
De nûmers binne net wichtich yn dit ferbân, wy binne ynteressearre yn de triemnamme.
Wat bart der úteinlik yn 'e sompe fan kommando's dy't begjinne op rigel 116?
Lit sjen.
It team bestiet út fjouwer dielen:
- Simple
echo
, dy't de wearde fan 'e fariabele printsje${VCL_SHOW}
echo "$VCL_SHOW"
awk
, dy't siket nei in rigel (rekord) dêr't it earste fjild, nei it brekken fan de tekst, "//", en de twadde is "VCL.SHOW".
Awk sil de earste rigel skriuwe dy't oerienkomt mei dizze patroanen en stopje dan fuortendaliks mei ferwurkjen.awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
- In blok koade dat fjildwearden opslacht yn fiif fariabelen, skieden troch spaasjes. De fyfde FILE fariabele ûntfangt de rest fan 'e rigel. Uteinlik skriuwt de lêste echo de ynhâld fan 'e fariabele út
${FILE}
.{ read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
- Sûnt alle stappen 1 oant 3 binne ynsletten yn in subshell, útfiert de wearde
$FILE
sil skreaun wurde nei in fariabeleVCL_FILE
.
Lykas it kommentaar op rigel 119 suggerearret, tsjinnet dit it ienige doel fan betroubere behanneling fan gefallen wêr't VCL bestannen sil ferwize mei spaasjes yn har nammen.
Ik haw kommentearre út de oarspronklike ferwurkjen logika foar ${VCL_FILE}
en besocht te feroarjen it kommando folchoarder, mar it hat net liede ta neat. Alles wurke goed foar my, mar doe't ik begon de tsjinst joech it in flater.
It liket derop dat de flater gewoan net reprodusearber is by it manuell útfieren fan it skript, wylst de sabeare 30 minuten al seis kear binne ferrûn en boppedat is in taak mei hegere prioriteit ferskynde, dy't oare saken oan 'e kant skowe. De rest fan de wike wie fol mei in ferskaat oan taken en waard mar in bytsje ferwettere troch in rapport oer sed en in fraachpetear mei in kandidaat. Probleem mei flater yn varnishreload
wie ûnherstelber ferlern yn it sân fan de tiid.
Dyn saneamde sed-fu... is eins... rommel
De oare wike hie ik noch in frij dei, dat ik besleat dit kaartsje wer oan te pakken. Ik hope dat yn myn harsens, guon eftergrûn proses hie socht nei in oplossing foar dit probleem al dizze tiid, en dizze kear soe ik perfoarst begripe wat der bart.
Om't gewoan feroarjen fan de koade de lêste kear net holp, haw ik gewoan besletten om it te herskriuwen fanôf rigel 116. Yn alle gefallen wie de besteande koade dom. En it is perfoarst net nedich om it te brûken read
.
Op 'e nij nei de flater sjen:
sh: echo: broken pipe
- echo ferskynt op twa plakken yn dit kommando, mar ik fermoedzje dat de earste is de wierskynliker skuldige (of op syn minst in meiwurker). Awk ynspireart ek gjin fertrouwen. En yn it gefal dat it echt is awk | {read; echo}
it ûntwerp liedt ta al dizze problemen, wêrom net ferfange? Dit kommando mei ien rigel brûkt net alle funksjes fan awk, en sels dizze ekstra read
derneist.
Sûnt ferline wike wie der in rapport oer sed
, Ik woe besykje myn nij opdien feardichheden en ferienfâldigje echo | awk | { read; echo}
yn in mear begryplik echo | sed
. Hoewol dit perfoarst net de bêste oanpak is om de brek te identifisearjen, tocht ik dat ik op syn minst myn sed-fu soe besykje en miskien wat nijs oer it probleem leare. Underweis frege ik myn kollega, de skriuwer fan it sedpraat, om my te helpen mei in effisjinter sedskript te kommen.
Ik liet de ynhâld falle varnishadm vcl.show -v "$VCL_NAME"
nei in bestân, dus ik koe my rjochtsje op it skriuwen fan it sed-skript sûnder gedoe mei it opnij opstarten fan 'e tsjinst.
In koarte beskriuwing fan krekt hoe't sed ynfier kin wurde fûn yn n
eksplisyt oantsjutte as in line separator.
Yn ferskate passaazjes en mei de oanbefellings fan myn kollega hawwe wy in sed-skript skreaun dat itselde resultaat joech as de hiele orizjinele rigel 116.
Hjirûnder is in foarbyldbestân mei ynfiergegevens:
> 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
Dit is miskien net dúdlik út 'e beskriuwing hjirboppe, mar wy binne allinich ynteressearre yn' e earste opmerking // VCL.SHOW
, en der kin ferskate fan harren yn de ynfier gegevens. Dit is wêrom de oarspronklike awk einiget nei de earste wedstriid.
# шаг первый, вывести только строки с комментариями
# используя возможности 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
Dat, de ynhâld fan it varnishreload-skript sil der sa útsjen:
VCL_FILE="$(echo "$VCL_SHOW" | sed -En '#// VCL.SHOW#{s#.*[0-9]+ [0-9]+ (.*)$#1#p;q;};')"
De boppesteande logika kin koart as folget wurde útdrukt:
As de tekenrige oerienkomt mei in reguliere ekspresje // VCL.SHOW
, fersoargje dan de tekst dy't beide nûmers yn dizze rigel befettet, en bewarje alles dat oerbliuwt nei dizze operaasje. Emit de opsleine wearde en einigje it programma.
Ienfâldich, is it net?
Wy wiene bliid mei it sed-skript en it feit dat it alle orizjinele koade ferfong. Al myn testen joegen de winske resultaten, dus ik feroare de "varnishreload" op 'e tsjinner en rûn it wer systemctl reload varnish
. Min flater echo: write error: Broken pipe
lake ús wer yn it gesicht. De knipperjende rinnerke wachte op in nij kommando om yn te fieren yn 'e tsjustere leechte fan' e terminal ...
Boarne: www.habr.com