"Buraya bakın" diyor, ekrandaki sembollerden birini işaret ederek, "Kırmızı şapkamla bahse girerim ki, size az önce gönderdiğim şeyi buraya eklersek," kodun başka bir bölümünü işaret ederek, "hata artık silinmeyecek. sergilenecek."
Biraz şaşkın ve yorgunum, bir süredir üzerinde çalıştığımız sed ifadesini değiştirip dosyayı kaydedip çalıştırıyorum systemctl varnish reload
. Hata mesajı kayboldu...
"Adayla paylaştığım e-postalar," diye devam etti meslektaşım, sırıtışı gerçek bir sevinç gülümsemesine dönüşürken, "Birdenbire bunun tamamen aynı sorun olduğunu fark ettim!"
Hepsi nasıl başladı
Makale bash, awk, sed ve systemd'nin nasıl çalıştığının anlaşıldığını varsayar. Vernik bilgisi tercih edilir ancak zorunlu değildir.
Parçacıklardaki zaman damgaları değiştirildi.
İle yazıldı
Bu metin, iki hafta önce İngilizce olarak yayınlanan orijinalin çevirisidir; tercüme
Yine sıcak bir sonbahar sabahında güneş panoramik pencerelerden parlıyor, taze hazırlanmış, kafein açısından zengin bir fincan içecek klavyeden uzakta duruyor, en sevdiğiniz ses senfonisi kulaklığınızda duyuluyor, mekanik klavyelerin hışırtısını bastırıyor ve ilk giriş Kanban panosundaki birikmiş talepler listesinde "Vernishreload'u araştırın" sh: echo: Evrelemede G/Ç hatası" (Aşamada "varnishreload sh: echo: G/Ç hatası"nı araştırın) kaçınılmaz başlığıyla şakacı bir şekilde parlıyor. Konu vernik olduğunda, bu durumda olduğu gibi sorun yaratmasa bile hataya yer yoktur, olamaz.
aşina olmayanlar için
Biletin adından da anlaşılacağı gibi hata sahnedeki sunuculardan birinde meydana geldi ve sahnedeki vernik yönlendirmesinin düzgün çalıştığından emin olduğum için bunun küçük bir hata olacağını varsaydım. Yani zaten kapalı olan bir çıktı akışıyla sonuçlanan bir mesaj. Bileti kendim için alıyorum, 30 dakikadan daha kısa bir süre içinde hazır olarak işaretleyeceğime tamamen güveniyorum, tahtayı bir başka çöpten temizlemek için sırtımı sıvazlayıp daha önemli konulara geri dönüyorum.
200 km/saat hızla duvara çarpmak
Dosyayı açma varnishreload
Debian Stretch çalıştıran sunuculardan birinde, 200 satırdan daha kısa bir kabuk betiği gördüm.
Komut dosyasını inceledikten sonra, onu doğrudan terminalden birden çok kez çalıştırırken sorunlara yol açabilecek hiçbir şey fark etmedim.
Sonuçta bu bir aşama, bozulsa bile kimse şikayet etmeyecek, yani... çok fazla değil. Komut dosyasını çalıştırıyorum ve terminale ne yazılacağını görüyorum, ancak hatalar artık görünmüyor.
Hatayı herhangi bir ek çaba harcamadan yeniden oluşturamayacağımdan emin olmak için birkaç çalıştırma daha yapıyorum ve bu betiği nasıl değiştireceğimi ve hala hata vermesini sağlayacağımı bulmaya başlıyorum.
Komut dosyası STDOUT'u geçersiz kılabilir mi (kullanarak > &-
)? Yoksa STDERR mi? Bunların hiçbiri sonunda işe yaramadı.
Görünüşe göre systemd bir şekilde başlangıç ortamını değiştiriyor, ama nasıl ve neden?
Vim'i açıp düzenliyorum varnishreload
, ekleme set -x
betiğin hata ayıklama çıktısının biraz ışık tutacağını umuyorum.
Dosya düzeltildi, bu yüzden verniği yeniden yükledim ve değişikliğin her şeyi tamamen bozduğunu görüyorum... Egzoz, içinde tonlarca C benzeri kodun bulunduğu tam bir karmaşa. Terminalde kaydırma yapmak bile nerede başladığını bulmak için yeterli değildir. Tamamen kafam karıştı. Hata ayıklama modu bir komut dosyasında başlatılan programların çalışmasını etkileyebilir mi? Hayır, bu saçmalık. Kabukta hata mı var? Kafamda farklı yönlere doğru giden hamamböcekleri gibi birçok olası senaryo uçuşuyor. Kafeinli içecek bardağı anında boşaltılıyor, stokları yenilemek için mutfağa hızlı bir yolculuk yapılıyor ve... yola çıkıyoruz. Senaryoyu açıyorum ve olaya daha yakından bakıyorum: #!/bin/sh
.
/bin/sh
- bu sadece bash'a bir sembolik bağlantıdır, dolayısıyla komut dosyası POSIX uyumlu modda yorumlanır, değil mi? Öyle değil! Debian'daki varsayılan kabuk dash'tır ve tam olarak buna benzemektedir. /bin/sh
.
# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 24 2017 /bin/sh -> dash
Bir test olarak shebang'ı şu şekilde değiştirdim: #!/bin/bash
, silindi set -x
ve tekrar denedim. Son olarak, verniğin daha sonra yeniden başlatılmasının ardından çıktıda kabul edilebilir bir hata ortaya çıktı:
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
Hat 124, işte burada!
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 }
Ancak 124. satırın oldukça boş olduğu ve ilgi çekici olmadığı ortaya çıktı. Hatanın yalnızca 116. satırdan başlayan çok satırlı bir dizenin parçası olarak oluştuğunu varsayabilirim.
Sonuçta değişkene ne yazılır? VCL_FILE
yukarıdaki alt kabuğun çalıştırılmasının bir sonucu olarak?
Başlangıçta değişkenin içeriğini gönderir VLC_SHOW
, boru aracılığıyla verilen komutun ardından 115. satırda oluşturuldu. Peki orada ne olur?
Öncelikle orada kullanılıyor varnishadm
Vernik kurulum paketinin bir parçası olan, yeniden başlatmaya gerek kalmadan vernik kurulumu için.
Alt takım vcl.show -v
belirtilen VCL konfigürasyonunun tamamının çıktısını almak için kullanılır. ${VCL_NAME}
, STDOUT'a.
Geçerli etkin VCL yapılandırmasının yanı sıra, hâlâ bellekte olan vernik yönlendirme yapılandırmalarının önceki birkaç sürümünü görüntülemek için şu komutu kullanabilirsiniz: varnishadm vcl.list
çıktısı aşağıdakine benzer olacaktır:
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
Değişken değer ${VCL_NAME}
betiğin başka bir bölümüne yüklenir varnishreload
Varsa, şu anda aktif olan VCL'nin adına. Bu durumda “reload_20190101_120000_12397” olacaktır.
Harika, değişken ${VCL_SHOW}
vernik için tam konfigürasyonu içerir, şimdilik açıktır. Şimdi nihayet kısa çizgi çıktısının neden olduğunu anlıyorum set -x
o kadar bozuk olduğu ortaya çıktı ki, ortaya çıkan konfigürasyonun içeriğini içeriyordu.
Tam bir VCL konfigürasyonunun genellikle birkaç dosyadan bir araya getirilebileceğini anlamak önemlidir. C tarzı yorumlar, belirli yapılandırma dosyalarının diğerlerinin neresine dahil edildiğini belirlemek için kullanılır ve aşağıdaki kod pasajının konusu da budur.
Dahil edilen dosyaları açıklayan yorumların sözdizimi aşağıdaki formattadır:
// VCL.SHOW <NUM> <NUM> <FILENAME>
Bu bağlamda sayılar önemli değil, dosya adıyla ilgileniyoruz.
116. satırdan başlayan komutlar bataklığında sonuçta ne olur?
Şunu kabul edelim ki.
Ekip dört bölümden oluşuyor:
- basit
echo
değişkenin değerini yazdıran${VCL_SHOW}
echo "$VCL_SHOW"
awk
metni böldükten sonra ilk alanın “//” ve ikincisinin “VCL.SHOW” olduğu bir satırı (kaydı) arar.
Awk, bu kalıplarla eşleşen ilk satırı yazacak ve ardından işlemeyi hemen durduracaktır.awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
- Alan değerlerini boşluklarla ayrılmış beş değişkende saklayan bir kod bloğu. Beşinci FILE değişkeni satırın geri kalanını alır. Son olarak, son yankı değişkenin içeriğini yazar
${FILE}
.{ read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
- 1'den 3'e kadar olan tüm adımlar bir alt kabuk içine alındığından, değerin çıktısı alınır.
$FILE
bir değişkene yazılacakVCL_FILE
.
119. satırdaki yorumun da belirttiği gibi, bu, yalnızca VCL'nin adlarında boşluk bulunan dosyalara referans vereceği durumların güvenilir bir şekilde ele alınması amacına hizmet eder.
Orijinal işleme mantığını yorumladım ${VCL_FILE}
ve komut sırasını değiştirmeye çalıştım ama hiçbir sonuca varamadım. Benim için her şey yolunda gitti ancak hizmeti başlattığımda hata verdi.
Görünüşe göre, komut dosyası manuel olarak çalıştırıldığında hata tekrarlanamaz, sözde 30 dakika zaten altı kez dolmuş ve buna ek olarak, diğer konuları bir kenara iterek daha yüksek öncelikli bir görev ortaya çıkmıştır. Haftanın geri kalanı çeşitli görevlerle doluydu ve sadece sed hakkında bir rapor ve bir adayla yapılan röportajla biraz sulandı. Hata ile ilgili sorun varnishreload
zamanın kumları arasında geri dönülemez bir şekilde kaybolmuştu.
Senin sözde sed-fu'n... aslında... saçmalık
Sonraki hafta oldukça boş bir günüm vardı, bu yüzden bu cezayı yeniden ele almaya karar verdim. Bunca zamandır beynimde bir arka plan sürecinin bu soruna çözüm aradığını ve bu sefer neler olup bittiğini kesinlikle anlayacağımı umuyordum.
Geçen sefer sadece kodu değiştirmek işe yaramadığından, 116. satırdan başlayarak yeniden yazmaya karar verdim. Her durumda, mevcut kod aptalcaydı. Ve kesinlikle kullanmaya gerek yok read
.
Hataya tekrar bakıyorum:
sh: echo: broken pipe
- echo bu komutta iki yerde görünüyor, ancak ilkinin daha muhtemel suçlu (veya en azından suç ortağı) olduğundan şüpheleniyorum. Awk da güven telkin etmiyor. Ve gerçekten öyle olması durumunda awk | {read; echo}
tasarım tüm bu sorunlara yol açıyor, neden onu değiştirmiyorsunuz? Bu tek satırlık komut, awk'nin tüm özelliklerini ve hatta bu ekstra özelliği bile kullanmaz read
Ek olarak.
Geçen haftadan bu yana bir rapor vardı. sed
, yeni edindiğim becerilerimi denemek ve basitleştirmek istedim echo | awk | { read; echo}
daha anlaşılır bir hale echo | sed
. Bu kesinlikle hatayı tanımlamak için en iyi yaklaşım olmasa da, en azından sed-fu'yu deneyebileceğimi ve belki sorun hakkında yeni bir şeyler öğrenebileceğimi düşündüm. Yol boyunca, sed konuşmasının yazarı olan meslektaşımdan daha verimli bir sed senaryosu geliştirmeme yardım etmesini istedim.
içindekileri bıraktım varnishadm vcl.show -v "$VCL_NAME"
Böylece hizmetin yeniden başlatılmasıyla uğraşmadan sed betiğini yazmaya odaklanabildim.
Sed'in girdiyi tam olarak nasıl işlediğine dair kısa bir açıklama şu adreste bulunabilir: n
açıkça satır ayırıcı olarak belirtilmiştir.
Birkaç geçişte ve meslektaşımın tavsiyeleriyle, orijinal 116 satırının tamamıyla aynı sonucu veren bir sed betiği yazdık.
Aşağıda giriş verilerini içeren örnek bir dosya bulunmaktadır:
> 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
Bu, yukarıdaki açıklamadan açıkça anlaşılmayabilir, ancak biz yalnızca ilk yorumla ilgileniyoruz // VCL.SHOW
ve giriş verilerinde bunlardan birkaçı olabilir. Bu nedenle orijinal awk ilk maçtan sonra sona eriyor.
# шаг первый, вывести только строки с комментариями
# используя возможности 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
Yani, vernishreload betiğinin içeriği şuna benzer:
VCL_FILE="$(echo "$VCL_SHOW" | sed -En '#// VCL.SHOW#{s#.*[0-9]+ [0-9]+ (.*)$#1#p;q;};')"
Yukarıdaki mantık kısaca şu şekilde ifade edilebilir:
Dize normal bir ifadeyle eşleşiyorsa // VCL.SHOW
, daha sonra bu satırdaki her iki sayıyı da içeren metni açgözlülükle yutun ve bu işlemden sonra kalan her şeyi kaydedin. Saklanan değeri yayınlayın ve programı sonlandırın.
Basit, değil mi?
Sed betiğinden ve tüm orijinal kodun yerini alması gerçeğinden memnunduk. Tüm testlerim istenen sonuçları verdi, bu yüzden sunucudaki “vernishreload”ı değiştirip tekrar çalıştırdım. systemctl reload varnish
. Büyük yanlış echo: write error: Broken pipe
yine yüzümüze güldü. Yanıp sönen imleç, terminalin karanlık boşluğunda yeni bir komutun girilmesini bekliyordu...
Kaynak: habr.com