$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли
öyle fortune
koşulsuz program exit(rand())
.
Açıklayabilir misin? burada sorun ne?
Lirik-tarihsel ara söz
Bu Heisenbug'la ilk kez çeyrek asır önce tanıştım. Daha sonra FaxNET'teki ağ geçidi için çeşitli yardımcı programların oluşturulması gerekliydi.
Sendmail ve uucp/uupc'deki hatalarla uğraşma konusundaki önceki deneyimim, "ayrıntılı hata yönetimi" konusundaki titizliğime katkıda bulundu. O hikayenin detaylarına dalmanın anlamı yok ama ben bu Heisenbug ile iki hafta boyunca 10-14 saat uğraştım. Bu nedenle hatırlandı ve dün bu eski tanıdık tekrar ziyarete uğradı.
TL;DR Cevap
Yarar head
kutu kanalı kapat fortune
kerede ilk satırı okur okumaz. Eğer fortune
birden fazla satırın çıktısını verir, ardından karşılık gelen çağrı write()
bir hata döndürecek veya istenenden daha az baytın çıktılandığını bildirecektir. Buna karşılık, dikkatli hata yönetimi ile yazılmıştır fortune
bu durumu çıkış durumuna yansıtma hakkına sahiptir. Daha sonra kurulum nedeniyle set -o pipefail
çalışacak || echo "Вы проиграли"
.
Bununla birlikte, head
zamanında yetişemeyebilirsin önce kapat fortune
veri çıktısını tamamlayacaktır. O zaman işe yarayacak && echo "Повезло!"
.
Bugünlerimden birinde GNUMakefile
echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'
İnsana tercüme
Burada yaygın bir durum --version
kim olduğunu sorar ve eğer seçenek desteklenmiyorsa bir taslak eklenir "Lütfen GCC veya CLANG uyumlu derleyiciyi kullanın".
gibi
#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"
Açıkçası eski “tanıdığımı” hemen tanıyamadım. Üstelik proje halihazırda Elbrus'ta ve Alt dahil birçok farklı dağıtım altında birçok kez test edildi. Çeşitli derleyiciler, GNU Make ve bash sürümleriyle. Bu nedenle hatamı burada görmek istemedim.
Sorunu yeniden oluşturmaya ve/veya neler olduğunu anlamaya çalışırken daha tuhaf şeyler olmaya başladı.
Komut satırı büyüsü:
echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"
Arada sırada fazladan metin üretiyordu, sonra çıkmıyordu... Çoğu zaman seçeneklerden biri oldukça uzun bir süre geçerli olurdu, ancak daha uzun süre uğraşırsanız her zaman ikisini de elde edersiniz!
Tabii ki, strace
Bu arada, kendine saygısı olan herkes gibi strace
ürememeyi tercih ediyor.
Yani, ne oluyor?
- Yarar
head
İstenilen satır sayısını okur okumaz okunmakta olan kanalı kapatma hakkına sahiptir (veya daha doğrusu zorlanır). - Veri üreten program yazarı (bu durumda
cc
) kutu birden fazla satır yazdırın ve özgür bunu birden fazla arama yoluyla yapınwrite()
. - Eğer okuyucunun, yazar tarafındaki kayıt bitmeden kendi tarafındaki kanalı kapatmak için zamanı olacak, ardından yazar bir hata alacaktır.
- Yazar programı hakkı var her ikisi de kanal yazma hatasını yok sayar ve bunu tamamlama kodunuza yansıtır.
- Kurulum nedeniyle
set -o pipefail
sonuç en az bir öğeden sıfır değilse boru hattı tamamlama kodu sıfır olmayacak (hatalı) ve sonra çalışacak|| echo "Please use GCC or CLANG compatible compiler"
.
Yazar programının sinyallerle nasıl çalıştığına bağlı olarak farklılıklar olabilir. Örneğin, program anormal bir şekilde sona erebilir (sıfır olmayan/hatalı sonlandırma durumunun otomatik olarak oluşturulmasıyla) veya write()
istenenden daha az bayt yazmanın sonucunu döndürecek ve ayarlayacaktır errno = EPIPE
.
Kim suçlu nedir?
Açıklanan durumda Her şeyden biraz. Hata işleme cc
(lcc:1.23.20:Sep—4-2019:e2k-v3-linux) değil gereksiz. Çoğu durumda dikkatli olmak daha iyidir, ancak bu, geleneksel davranış için tasarlanmış standartlarda ani kusurları ortaya çıkarır.
Ne yapmalı?
yanlış:
fortune | head -1 && echo "Повезло, но вы рискуете!" || echo "WTF?"
düzeltin:
-
(fortune && echo "Успешно" || echo "Ошибка") | head -1
Burada, iç içe geçmiş boru hattına servis yapılırken bir borunun erken kapatılması komut yorumlayıcısı tarafından yönetilecektir ("parantez içinde"). Buna göre eğer
fortune
durumdaki kapalı bir kanala yazılı olarak bir hata bildirecek, ardından çıktı|| echo "Ошибка"
Kanal zaten kapalı olduğu için hiçbir yere varamayacak. -
fortune | cat - | head -1 && echo "Успешно" || echo "Ошибка"
İşte yardımcı program
cat
hatayı görmezden geldiği için sönümleyici görevi görürEPIPE
çekilme üzerine. Şimdilik bu kadar sonuç yeterlifortune
küçüktür (birkaç satır) ve kanal arabelleğine sığar (512 bayttan ≈64K'ya, çoğu işletim sisteminde ≥4K). Aksi takdirde sorun geri dönebilir.
Doğru şekilde nasıl işlenir EPIPE
ve diğer kayıt hataları?
Tek bir doğru çözüm yoktur ancak basit öneriler vardır:
EPIPE
gereken işlenmeli (ve çıkış durumuna yansıtılır) bütünlük gerektiren verilerin çıktısı alınırken. Örneğin arşivleyicilerin veya yedekleme yardımcı programlarının çalışması sırasında.EPIPE
görmezden gelmek daha iyi bilgi ve yardımcı mesajları görüntülerken. Örneğin seçeneklerle ilgili bilgileri görüntülerken--help
veya--version
.- Geliştirilmekte olan kod daha önce bir boru hattında kullanılabiliyorsa
| head
SonraEPIPE
Görmezden gelmek daha iyidir, aksi takdirde işlenip çıkış durumuna yansıtılması daha iyidir.
Bu fırsatı kullanarak ekiplere şükranlarımı sunmak isterim.
Devam edin Camarades, yukarı
Teşekkürler
KDPV'den
Kaynak: habr.com