SIGINT ve benzerlerini yakalarken çıkış kodları saklansın mı?


14

Eğer ctrl-c (veya benzeri) yakalamak ve çıkmadan önce temizlemek için trapörneğin http://linuxcommand.org/wss0160.php#trap açıklandığı gibi kullanırsanız o zaman iade çıkış kodunu değiştiriyorum.

Şimdi bu muhtemelen gerçek dünyada bir fark yaratmayacaktır (örneğin, çıkış kodları taşınabilir olmadığından ve bunun üzerine , işlem sonlandırıldığında Varsayılan çıkış kodunda tartışıldığı gibi her zaman açık değil mi? ) Ama yine de var olup olmadığını merak ediyorum gerçekten bunu önlemek ve yerine kesintiye komut dosyaları için varsayılan hata kodunu döndürmek için bir yolu yok?

Örnek (bash dilinde, ancak sorum bash'a özgü sayılmamalıdır):

#!/bin/bash
trap 'echo EXIT;' EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. ' _
trap 'echo SIGINT; exit 1;' INT
read -p 'If you ctrl-c me now my return code will be 1. ' _

Çıktı:

$ ./test.sh # doing ctrl-c for 1st read
If you ctrl-c me now my return code will be the default for SIGTERM.
$ echo $?
130
$ ./test.sh # doing ctrl-c for 2nd read
If you ctrl-c me now my return code will be the default for SIGTERM.
If you ctrl-c me now my return code will be 1. SIGINT 
EXIT
$ echo $?
1

(Daha fazla POSIX uyumlu hale getirmek için kaldırılmak üzere düzenlenmiştir.)

(Bunun yerine bir bash betiği yapmak için tekrar düzenlendi, soruma kabuk özel değil.)

Taşınabilir olmayan "SIGINT" lehine tuzak için taşınabilir "INT" kullanmak üzere düzenlenmiştir.

İşe yaramaz kıvırcık parantezleri kaldırmak ve potansiyel çözüm eklemek için düzenlenmiştir.

Güncelleme:

Şimdi sadece kodlanmış bazı hata kodları ile çıkıp EXIT yakalayarak çözdüm. Bu, bazı sistemlerde sorunlu olabilir, çünkü hata kodu farklı olabilir veya EXIT tuzağı mümkün olmayabilir, ancak benim durumumda yeterince iyi.

trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM

Betiğiniz biraz garip görünüyor: readgeçerli işlemden okumayı trap cmd SIGINTsöylüyorsunuz ve standart kullanmanız gerektiğini söylediği gibi çalışmayacak trap cmd INT.
schily

Ah evet, POSIX altında elbette SIG öneki olmadan.
phk

Maalesef "read -p" de desteklenmeyecek, bu yüzden bash için uyarlayacağım.
phk

@schily: Yine de "coprocess" ile ne demek istediğini bilmiyorum.
phk

Peki, Korn Shell el sayfası read -pşu anki işlemden girdi okuduğunu söylüyor .
schily

Yanıtlar:


4

Tek yapmanız gereken temizleme işleyicinizin içindeki EXIT işleyicisini değiştirmek . İşte bir örnek:

#!/bin/bash
cleanup() {
    echo trapped exit
    trap 'exit 0' EXIT
}
trap cleanup EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. '

trap cleanup INTyerine bunu mu demek istediniz trap cleanup EXIT?
Jeff Schaller

Bence EXIT demek istedim. Çıkış nihayet çağrıldığında, ancak bu yapılır, 0 dönüşüyle ​​çıkmak için tuzağı değiştiririz. Sinyal işleyicilerinin özyinelemesine inanmıyorum.
kayalık

Tamam, senin örnekte ben hiç (SIG) INT tuzak değilim aslında kullanıcı ctrl-c üzerinden benim interaktif script çıkarken bile bazı temizlik yapmak için ne istiyorum.
phk

@phk Tamam. Ben çıkış 0 geri dönmek için zor nasıl ya da topladığım sorun olduğunu başka bir değer hakkında fikir olsun düşünüyorum. SIGINT tarafından çağrılan gerçek temizleme işleyicinizin içine tuzak EXIT ayarını koymak için kodu ayarlayabileceğinizi varsayalım.
kayalık

@rocky: Amacım, normalde işleyici olmadan sahip olacağım çıkış kodunu değiştirmezken bir tuzak işleyicisine sahip olmaktı. Şimdi sadece normalde alacağınız çıkış kodunu döndürebilirim ama sorun bu durumlarda tam çıkış kodunu bilmememdi (bağlandığım iş parçacığında ve meuh'tan gelen mesajda oldukça karmaşıktı). Yayınınız hala yardımcı oldu, tuzak işleyiciyi dinamik olarak yeniden tanımlamayı düşünmedim.
phk

9

Aslında, bash'ın iç kısmının kesilmesi, bash readtarafından çalıştırılan bir komutun kesilmesinden biraz farklı gibi görünüyor. Girdiğinizde Normalde trap, $?ayarlanır ve bunu aynı değere sahip çıkmak koruyabilir:

trap 'rc=$?; echo $rc SIGINT; exit $rc' INT
trap 'rc=$?; echo $rc EXIT; exit $rc' EXIT

Komut dosyası gibi bir komut sleep veya hatta yerleşik bir komut yürütülürken kesintiye uğrarsa wait,

130 SIGINT
130 EXIT

ve çıkış kodu 130'dur. Ancak, 0 read -pgibi görünüyor $?(yine de bash 4.3.42 sürümümde).


Yayınımdaki readdeğişiklik dosyasına göre , sinyallerin işlenmesi devam ediyor olabilir ... (/ usr / share / doc / bash / CHANGES)

bu sürüm, bash-4.3-alfa ve önceki sürüm, bash-4.2-release arasında değişir.

  1. Bash'deki Yeni Özellikler

    r. Posix modundayken, `` okuma '' sıkışmış bir sinyal tarafından kesilebilir. Tuzak işleyiciyi çalıştırdıktan sonra, okuma 128 + sinyalini döndürür ve kısmen okunan girişleri atar.


Tamam, bu çok garip. 4.3.42 bash (cygwin altında) üzerinde etkileşimli kabuk üzerinde 130, bir komut dosyası ve POSIX modunda aynı kabuk altında 0 veya fark etmez. Ama sonra çizgi ve
meşgul kutusu

POSIX modunu çıkmayan bir tuzakla denedim ve readCHANGES dosyasında (cevabıma eklendi) belirtildiği gibi yeniden başlatıyor. Yani, devam eden bir çalışma olabilir.
meuh

Çıkış kodu 130,% 100 taşınabilir değildir. Bourne Kabuğu 128 + signosinyaller için çıkış kodu olarak kullanılırken, ksh93 kullanır 256 + signo. POSIX diyor ki: 128'in üstünde bir şey ....
schily

@schily Doğru, orijinal gönderide bağlandığım iş parçacığında belirtildiği gibi ( unix.stackexchange.com/questions/99112 ).
phk

6

Herhangi bir olağan sinyal çıkış kodu $?, tuzak işleyiciye girişte mevcut olacaktır :

sig_handler() {
    exit_status=$?  # Eg 130 for SIGINT, 128 + (2 == SIGINT)
    echo "Doing signal-specific up"
    exit "$exit_status"
}
trap sig_handler INT HUP TERM QUIT

Ayrı bir EXIT tuzağı varsa, aynı metodolojiyi kullanabilirsiniz: sinyal işleyicisinden (varsa) çıkış durumunu hemen temiz tutun, ardından kaydedilen çıkış durumunu döndürün.


Bu çoğu mermi için mi geçerlidir? Çok hoş. localPOSIX değil.
phk

1
Düzenlenen. Ben dışında test etmedim {ba,z}sh. AFAIK, ne olursa olsun yapılabilecek en iyisidir.
Tom Hale

Bu çizgi içinde çalışmaz ( $?alınan sinyal ne olursa olsun 1'dir).
MoonSweep

2

Sadece bazı hata kodlarını iade edin, SIGINT tarafından bir çıkışı simüle etmek için yeterli değildir. Şimdiye kadar kimsenin bundan bahsetmediğine şaşırdım. Daha fazla okuma: https://www.cons.org/cracauer/sigint.html

Doğru yol:

for sig in EXIT ABRT HUP INT PIPE QUIT TERM; do
    trap "cleanup;
          [ $sig  = EXIT ] && normal_exit_only_cleanup;
          [ $sig != EXIT ] && trap - $sig EXIT && kill -s $sig $$
         " $sig
done

Bu Bash, Dash ve zsh ile çalışır. Daha fazla taşınabilirlik için sayısal sinyal özelliklerini kullanmanız gerekir (diğer yandan zsh, killkomut için bir dize parametresi bekler ...)

Ayrıca EXITsinyalin özel tedavisine de dikkat edin . Bunun nedeni, EXITherhangi bir sinyal için tuzaklar yürüten bazı kabuklardan (yani Bash) kaynaklanmaktadır (muhtemelen bu sinyal üzerinde tanımlanmış tuzaklardan sonra). Tuzağın sıfırlanması EXITbunu engeller.

Kodu yalnızca "normal" çıkışta yürütme

Kontrol [ $sig = EXIT ], kodun yalnızca normal (sinyalsiz) çıkışta yürütülmesine izin verir. Ancak, tüm sinyallerin en sonunda bindirmeyi sıfırlayan bindirmeleri olmalıdır EXIT; normal_exit_only_cleanupayrıca sinyaller için çağrılır. Ayrıca üzerinden bir çıkışla yürütülür set -e. Bu, ERR(ile Dash tarafından desteklenmez) üzerine bindirilerek ve simgesinden [ $sig = ERR ]önce bir kontrol eklenerek düzeltilebilir kill.

Basitleştirilmiş Bash sürümü

Öte yandan, bu davranış, Bash'te

trap cleanup EXIT

sadece bazı temizleme kodlarını yürütün ve çıkış durumunu koruyun.

düzenlenmiş

  • Ayrıntılı Bash'in "EXIT her şeyi tuzağa düşürür" davranışı

  • Sıkışamayan KILL sinyalini kaldırın

  • Sinyal adlarından SIG öneklerini kaldır

  • Yapmaya kalkma kill -s EXIT

  • set -e/ ERR'yi dikkate alın


Sinyalleri yakalayan bir programın, sinyal aldığı gerçeğini koruyacak şekilde sona erdirilmesine ilişkin genel bir gereklilik yoktur. Örneğin, bir program göndermenin SIGINTonu kapatmanın bir yolu olduğunu ilan edebilir ve hatasız olarak sonlandırmayı başardıysa 0 ile veya temiz kapanmadığı takdirde başka bir hata kodu ile çıkmaya karar verebilir. Tipik bir örnek: top. Çalıştırın top; echo $?ve sonra Ctrl-C'ye basın. Ekrana atılan durum 0 olacaktır.
Louis

1
Bunu işaret ettiğiniz için teşekkür ederim. Ancak, poster özellikle çıkış kodunun saklanmasını sordu .
philipp2100
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.