Bir program stdout'un bir terminale veya bir boruya bağlı olup olmadığını nasıl bilebilir?


12

Segfault hemen önce çıkış ne gerek, çünkü bir segfaulting program hata ayıklama sorun yaşıyorum, ama bir dosyaya çıkış boru eğer bu kaybolur. Bu cevaba göre: /unix//a/17339/22615 , bunun nedeni programın çıkış arabelleğinin bir terminale bağlandığında hemen temizlenmesi, ancak bir boruya bağlandığında sadece belirli noktalarda temizlenmesi. Burada birkaç soru:

  • Bir program stdout'unun neye bağlı olduğunu nasıl belirler?

  • "Script" komutu programın bir terminale yazmasıyla aynı davranışı nasıl üretir?

  • Bu script komutu olmadan gerçekleştirilebilir mi?


Yanıtlar:


23

Bir dosya tanımlayıcısının terminal aygıtını gösterip göstermediğini söyleme

Bir program, bir dosya tanımlayıcının isatty()standart C işlevini kullanarak bir tty cihazıyla ilişkilendirilip ilişkilendirilmediğini söyleyebilir (genellikle altında ioctl(), fd bir tty cihazını işaret etmediğinde bir hata ile geri dönecek zararsız tty'ye özgü bir sistem çağrısı yapar) .

[/ testYarar onun ile bunu yapabilir -toperatörü.

if [ -t 1 ]; then
  echo stdout is open to a terminal
fi

Bir GNU / Linux sisteminde libc fonksiyon çağrılarını izleme:

$ ltrace [ -t 1 ] | cat
[...]
isatty(1)                                      = 0
[...]

İzleme sistemi çağrıları:

$ strace [ -t 1 ] | cat
[...]
ioctl(1, TCGETS, 0x7fffd9fb3010)        = -1 ENOTTY (Inappropriate ioctl for device)
[...]

Bir boruyu gösterip göstermediğini söyleme

Bir fd'nin bir pipe / fifo ile ilişkilendirilip ilişkilendirilmediğini belirlemek için , alanı o fd'de açılan dosyanın türünü ve izinlerini içeren bir yapı döndüren fstat()sistem çağrısı kullanılabilir st_mode. S_ISFIFO()Standart C makro bu kullanılabilir st_modefd FIFO / bir boru olup olmadığını belirlemek için alan.

Standart bir yardımcı program yoktur fstat(), ancak statbunu yapabilen bir komutun birkaç uyumsuz uygulaması vardır . zshVar statyerleşik olan stat -sf "$fd" +modeilk karakter (tip temsil eden bir dizi temsili olarak moduna döner olan pboru için). GNU statda aynısını yapabilir stat -c %A - <&"$fd", fakat aynı zamanda türü tek başına stat -c %F - <&"$fd"rapor etmek zorundadır . BSD ile : veya .statstat -f %St <&"$fd"stat -f %HT <&"$fd"

Aranabilir olup olmadığını söylemek

Uygulamalar genellikle stdout'un bir boru olup olmadığını umursamaz. Araştırılabilir olmasını umabilirler (genellikle tampon olup olmayacağına karar vermemekle birlikte).

Bir fd'nin aranıp aranmadığını test etmek için (borular, soketler, tty cihazları aranamaz, normal dosyalar ve çoğu blok cihazı genellikle), 0 ofseti ile göreceli bir lseek()sistem çağrısı girişiminde bulunabilir (çok zararsız). ddbir arabirim olan standart bir yardımcı programdır, lseek()ancak lseek()0 dengesi isterseniz uygulamalar hiç çağrılamayacağından, bu test için kullanılamaz .

zshVe ksh93kabukları olsa operatörleri arayan yerleşik vardır:

$ strace -e lseek ksh -c ': 1>#((CUR))' | cat
lseek(1, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
ksh: 1: not seekable
$ strace -e lseek zsh -c 'zmodload zsh/system; sysseek -w current -u 1 0 || syserror'
lseek(1, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
Illegal seek

Arabelleğe almayı devre dışı bırakma

scriptProgramın stdout'u (ve Stdin ve stderr) bir psödo-terminal cihazı olacak şekilde, komut, bir programın çıkışı yakalamak için bir sözde-terminal çift kullanır.

Stdout bir terminal cihazında olduğunda, hala genel olarak bir miktar tamponlama vardır, ancak hat tabanlıdır. printf/ putsve co, yeni satır karakteri çıkana kadar hiçbir şey yazmaz. Diğer dosya türleri için arabelleğe alma, bloklarla yapılır (birkaç kilo baytlık).

/ / / Tarafından yapılabileceği gibi bir sözde terminal kullanarak , burada bir dizi Soru & Cevap bölümünde tartışılan tamponlamayı devre dışı bırakmak için birkaç seçenek vardır ( tamponlamayı veya stdbuf'u ara , Kesme çıktısını yeniden yönlendiremiyor birkaç yaklaşım sağlar). (bir komut dosyası) / ' veya GNU'lar veya FreeBSD'ler tarafından yapılan şekilde arabelleğe almayı devre dışı bırakmak için yürütülebilir dosyaya kod enjekte ederek .socatscriptexpectunbufferexpectzshzptystdbuf


1
Harika cevap, bunun için çok teşekkür ederim!
mowwwalker

Linux'a özgü başka bir yaklaşım, /procdizini çaprazlamaktır ve her /proc/<integer>/dizin için serverfault.com/q/48330/363611/proc/<integer>/fd/ dosyasında aynı inode numarasına sahip dosya tanımlayıcıya bakıp bulmaktır , ancak bu yalnızca komut dosyalarında açıklanan sistem çağrılarını kullanamadığında yararlıdır Stephane cevabı ve daha uygun bir çözüm daha geçici bir çözüm IMHOpipefs
Sergiy Kolodyazhnyy

BSD'de, lseekterminallerde ve diğer karakter cihazlarında başarılı olacak ve her başarılı okumada artan bir sayacı yeniden ayarlayacaktır (). Bunun "aranabilir" olup olmadığını bilmiyorum.
Mosvy

@mowwwalker Bu yanıt sorununuzu çözdüyse, lütfen bir dakikanızı ayırın ve soldaki onay işaretini tıklayarak kabul edin. Bu, soruyu yanıtlandığı gibi işaretler ve Stack Exchange sitelerinde teşekkürlerin ifade edilme şeklidir.
tatlı
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.