"Rigardu ĉi tien," li diras, montrante unu el la simboloj sur la ekrano, "Mi vetas mian ruĝan ĉapelon, ke se ni aldonos ĉi tie tion, kion mi ĵus sendis al vi," indikante alian sekcion de kodo, "la eraro ne plu estos. estos montrata."
Iom konfuzita kaj laca, mi modifas la sed-esprimon, pri kiu ni laboris dum kelka tempo, konservas la dosieron kaj kuras systemctl varnish reload
. La erarmesaĝo malaperis...
"La retpoŝtojn, kiujn mi interŝanĝis kun la kandidato," daŭrigis mia kolego, dum lia rido kreskis en aŭtentan rideton de ĝojo, "subite ekkomprenis, ke ĉi tio estas ĝuste la sama problemo!"
Kiel ĉio komenciĝis
La artikolo supozas komprenon pri kiel bash, awk, sed kaj systemd funkcias. Scio pri verniso estas preferita, sed ne postulata.
Tempmarkoj en fragmentoj estis ŝanĝitaj.
Skribita kun
Ĉi tiu teksto estas traduko de la originalo publikigita en la angla antaŭ du semajnoj; traduko
La suno brilas tra la panoramaj fenestroj en alia varma aŭtuna mateno, taso da freŝe preparita kafein-riĉa trinkaĵo ripozas for de la klavaro, via plej ŝatata simfonio de sonoj sonas en viaj aŭdiloj, dronigante la susuradon de mekanikaj klavaroj, kaj la unua eniro. en la listo de backlog-biletoj sur la Kanban-tabulo lude brilas kun la fatala titolo "Enketi varnishreload" sh: echo: I/O-eraro en enscenigo" (Enketu "varnishreload sh: echo: I/O-eraro" en enscenigo). Kiam temas pri verniso, estas kaj ne povas esti loko por eraroj, eĉ se ili ne rezultigas problemojn kiel en ĉi tiu kazo.
Por tiuj, kiuj ne konas
Kiel la titolo de la bileto sugestas, la eraro okazis sur unu el la serviloj sur la scenejo, kaj ĉar mi estis certa, ke la varniĉa vojo sur la scenejo funkcias ĝuste, mi supozis, ke tio estus eta eraro. Do, nur mesaĝo kiu finiĝis en jam fermita eligofluo. Mi prenas la bileton por mi, tute konfidante, ke mi markos ĝin preta en malpli ol 30 minutoj, frapos min la dorson por purigi la tabulon de ankoraŭ alia rubo kaj revenos al pli gravaj aferoj.
Frakasi kontraŭ muro je 200 km/h
Malfermante la dosieron varnishreload
, sur unu el la serviloj kurantaj Debian Stretch, mi vidis ŝelan skripton longan malpli ol 200 liniojn.
Traleginte la skripton, mi ne rimarkis ion ajn, kio povus rezultigi problemojn kiam ĝi ruliĝas plurfoje rekte de la terminalo.
Ja tio ĉi estas etapo, eĉ se ĝi rompas, neniu plendos, nu... ne tro. Mi rulas la skripton kaj vidas, kio estos skribita al la terminalo, sed la eraroj ne plu videblas.
Kelkaj pliaj kuroj por certigi, ke mi ne povas reprodukti la eraron sen plia peno, kaj mi komencas eltrovi kiel ŝanĝi ĉi tiun skripton kaj igi ĝin ankoraŭ ĵeti eraron.
Ĉu la skripto povas anstataŭi STDOUT (uzante > &-
)? Aŭ STDERR? Neniu el ĉi tiuj funkciis finfine.
Ŝajne systemd iel modifas la startan medion, sed kiel, kaj kial?
Mi malfermas vim kaj redaktas varnishreload
, aldonante set -x
ĝuste sub la shebang, esperante ke la sencimiga eligo de la skripto ĵetos iom da lumo.
La dosiero estas korektita, do mi reŝargas vernison kaj vidas, ke la ŝanĝo tute rompis ĉion... La ellasilo estas kompleta malordo, en kiu estas tunoj da C-simila kodo. Eĉ movo en la terminalo ne sufiĉas por trovi kie ĝi komenciĝas. Mi estas tute konfuzita. Ĉu sencimiga reĝimo povas influi la funkciadon de programoj lanĉitaj en skripto? Ne, ĝi estas sensencaĵo. Cimo en la ŝelo? Pluraj eblaj scenaroj kuregas tra mia kapo kiel blatoj en malsamaj direktoj. La taso de la kafeina trinkaĵo estas tuj malplenigita, rapida vojaĝo al la kuirejo por replenigi la stokon kaj... foriras. Mi malfermas la skripton kaj pli detale rigardas la shebang: #!/bin/sh
.
/bin/sh
- ĉi tio estas nur simbolligo al bash, do la skripto estas interpretita en POSIX-kongrua reĝimo, ĉu ne? Ne tiel! La defaŭlta ŝelo sur Debian estas dash, kaj ĝuste tiel ĝi aspektas. /bin/sh
.
# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 24 2017 /bin/sh -> dash
Kiel testo, mi ŝanĝis la shebang al #!/bin/bash
, forigita set -x
kaj provis denove. Finfine, post posta rekomenco de verniso, tolerebla eraro aperis en la eligo:
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
Linio 124, jen ĝi!
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 }
Sed kiel ĝi rezultas, linio 124 estas sufiĉe malplena kaj sen intereso. Mi nur povis supozi, ke la eraro okazis kiel parto de plurlinia ĉeno komencanta sur linio 116.
Kio estas finfine skribita al la variablo? VCL_FILE
kiel rezulto de ekzekuto de la supra subŝelo?
Komence, ĝi sendas la enhavon de la variablo VLC_SHOW
, kreita sur linio 115, sekvante la komandon tra la pipo. Kaj tiam kio okazas tie?
Unue, ĝi estas uzata tie varnishadm
, kiu estas parto de la varnish-instala pako, por agordi varnikon sen rekomenco.
Subteamo vcl.show -v
uzata por eligi la tutan VCL-agordon specifitan en ${VCL_NAME}
, al STDOUT.
Por montri la nunan aktivan VCL-agordon, same kiel plurajn antaŭajn versiojn de varnish-vojaj agordoj, kiuj ankoraŭ estas en memoro, vi povas uzi la komandon varnishadm vcl.list
, kies eligo estos simila al tiu ĉi sube:
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
Varia valoro ${VCL_NAME}
estas instalita en alia parto de la skripto varnishreload
al la nomo de la aktuale aktiva VCL, se ekzistas unu. En ĉi tiu kazo ĝi estos "reload_20190101_120000_12397".
Granda, varia ${VCL_SHOW}
enhavas kompletan agordon por verniso, klara nuntempe. Nun mi finfine komprenas kial la streka eligo estas set -x
montriĝis tiel rompita - ĝi inkludis la enhavon de la rezulta agordo.
Gravas kompreni, ke kompleta VCL-agordo ofte povas esti kunmetita el pluraj dosieroj. C-stilaj komentoj estas uzataj por identigi kie certaj agordaj dosieroj estis inkluzivitaj en aliaj, kaj pri tio temas la sekva linio de koda fragmento.
La sintakso por komentoj priskribantaj inkluzivitajn dosierojn estas en la sekva formato:
// VCL.SHOW <NUM> <NUM> <FILENAME>
La nombroj ne gravas en ĉi tiu kunteksto, ni interesiĝas pri la dosiernomo.
Kio finfine okazas en la marĉo de komandoj ekde la linio 116?
Ni eltrovu ĝin.
La teamo konsistas el kvar partoj:
- Simpla
echo
, kiu presas la valoron de la variablo${VCL_SHOW}
echo "$VCL_SHOW"
awk
, kiu serĉas linion (rekordon) kie la unua kampo, post rompado de la teksto, estas “//”, kaj la dua estas “VCL.SHOW”.
Awk skribos la unuan linion kongruan kun ĉi tiuj ŝablonoj kaj poste ĉesos la prilaboradon tuj.awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
- Bloko de kodo, kiu stokas kampajn valorojn en kvin variabloj, apartigitaj per spacoj. La kvina FILE-variablo ricevas la reston de la linio. Fine, la lasta eĥo skribas la enhavon de la variablo
${FILE}
.{ read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
- Ĉar ĉiuj paŝoj 1 ĝis 3 estas enfermitaj en subŝelo, eligante la valoron
$FILE
estos skribita al variabloVCL_FILE
.
Kiel la komento sur linio 119 sugestas, tio servas al la sola celo de fidinde trakti kazojn kie VCL referencos dosierojn kun spacoj en iliaj nomoj.
Mi komentis la originalan prilaboran logikon por ${VCL_FILE}
kaj provis ŝanĝi la ordonsekvencon, sed ĝi ne kondukis al io ajn. Ĉio funkciis bone por mi, sed kiam mi komencis la servon, ĝi donis eraron.
Ŝajnas, ke la eraro simple ne estas reproduktebla dum rulado de la skripto permane, dum la supozitaj 30 minutoj jam eksvalidiĝis ses fojojn kaj krome aperis pli alta prioritata tasko, flankenpuŝante aliajn aferojn. La resto de la semajno estis plenigita de diversaj taskoj kaj estis nur iomete diluita de raporto pri sed kaj intervjuo kun kandidato. Problemo kun eraro en varnishreload
estis nereveneble perdita en la sabloj de la tempo.
Via tiel nomata sed-fu... efektive estas... rubo
La sekvan semajnon mi havis unu sufiĉe liberan tagon, do mi decidis denove trakti ĉi tiun bileton. Mi esperis, ke en mia cerbo, iu fona procezo serĉis solvon al ĉi tiu problemo dum ĉi tiu tempo, kaj ĉi-foje mi certe komprenos kio okazas.
Ĉar simple ŝanĝi la kodon lastan fojon ne helpis, mi ĵus decidis reverki ĝin ekde la linio 116. Ĉiukaze, la ekzistanta kodo estis stulta. Kaj ne necesas uzi ĝin read
.
Rigardante la eraron denove:
sh: echo: broken pipe
— eĥo aperas duloke en ĉi tiu ordono, sed mi suspektas, ke la unua estas la pli verŝajna kulpulo (aŭ almenaŭ komplico). Awk ankaŭ ne inspiras konfidon. Kaj se ĝi vere estas awk | {read; echo}
la dezajno kondukas al ĉiuj ĉi tiuj problemoj, kial ne anstataŭigi ĝin? Ĉi tiu unulinia komando ne uzas ĉiujn funkciojn de awk, kaj eĉ ĉi tiun kroman read
krome.
Ekde la pasinta semajno estis raporto pri sed
, Mi volis provi miajn nove akiritajn kapablojn kaj simpligi echo | awk | { read; echo}
en pli kompreneblan echo | sed
. Kvankam ĉi tio certe ne estas la plej bona aliro por identigi la cimon, mi pensis, ke mi almenaŭ provus mian sed-fu kaj eble lernus ion novan pri la problemo. Survoje mi petis mian kolegon, la aŭtoron de la sed-parolado, ke mi helpu min elpensi pli efikan sed-skripton.
Mi faligis la enhavon varnishadm vcl.show -v "$VCL_NAME"
al dosiero, do mi povus koncentriĝi pri verkado de la sed-skripto sen ia ĝeno de servaj rekomencoj.
Mallonga priskribo de ekzakte kiel sed prilaboras enigaĵon troveblas n
eksplicite specifita kiel linia apartigilo.
En pluraj paŝoj kaj kun la rekomendoj de mia kolego, ni skribis sed-skripton, kiu donis la saman rezulton kiel la tuta originala linio 116.
Malsupre estas ekzempla dosiero kun enirdatumoj:
> 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
Ĉi tio eble ne estas evidenta el la supra priskribo, sed ni interesiĝas nur pri la unua komento // VCL.SHOW
, kaj povas esti pluraj el ili en la enirdatenoj. Tial la originala awk finiĝas post la unua matĉ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
Do, la enhavo de la varnishreload skripto aspektos kiel ĉi tio:
VCL_FILE="$(echo "$VCL_SHOW" | sed -En '#// VCL.SHOW#{s#.*[0-9]+ [0-9]+ (.*)$#1#p;q;};')"
Ĉi-supra logiko povas esti mallonge esprimita jene:
Se la ĉeno kongruas kun regula esprimo // VCL.SHOW
, tiam avide formanĝu la tekston, kiu inkluzivas ambaŭ ciferojn en ĉi tiu linio, kaj konservu ĉion, kio restas post ĉi tiu operacio. Elsendu la konservitan valoron kaj ĉesigu la programon.
Simpla, ĉu ne?
Ni estis feliĉaj kun la sed-skripto kaj la fakto ke ĝi anstataŭigis la tutan originalan kodon. Ĉiuj miaj provoj donis la deziratajn rezultojn, do mi ŝanĝis la "varnishreload" sur la servilo kaj kuris ĝin denove systemctl reload varnish
. Malbona eraro echo: write error: Broken pipe
denove ridis en niajn vizaĝojn. La palpebruma kursoro atendis ke nova komando estos enigita en la malluma malpleno de la terminalo...
fonto: www.habr.com