Bir dosya tanımlayıcının geçerli olup olmadığını test etme


12

Bir bash komut dosyası çıktısı, açık olduklarında dosya tanımlayıcılarına (FD) 3 veya daha büyük ek bilgiler yapmak istiyorum. Bir FD'nin açık olup olmadığını test etmek için aşağıdaki hileyi tasarladım:

if (printf '' 1>&3) 2>&-; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi

Bu benim ihtiyaçlarım için yeterli, ama bir FD geçerli olup olmadığını daha deyimsel bir test yolu olup olmadığını merak ediyorum. Özellikle bir eşleştirmesini bulunup bulunmadığına dair ilgilenen ediyorum fcntl(1)(FD bayrakları alınmasını sağlayacak bir kabuk komutuna syscall, O_WRONLYve O_RDWRFD yazılabilir olup olmadığını test etmek için, ve O_RDONLYve O_RDWRFD okunabilir olup olmadığını test etmek için).

Yanıtlar:


12

In ksh(hem AT & T ve pdksh varyantları) veya zshyapabileceğiniz:

if print -nu3; then
  echo fd 3 is writeable
fi

Bu fd üzerine hiçbir şey yazmazlar, ancak yine de fd'nin yazılabilir (kullanılıp kullanılmadığını fcntl(3, F_GETFL)) kontrol eder ve bir hata raporlar:

$ ksh -c 'print -nu3' 3< /dev/null
ksh: print: -u: 3: fd not open for writing

(buna yönlendirebilirsiniz /dev/null).

İle bash, tek seçeneğiniz dup()yaklaşımınızdaki gibi başarılı olup olmadığını kontrol etmek olduğunu düşünüyorum , ancak fd'nin yazılabilir olduğunu garanti etmeyecektir (veya yapmak için harici bir yardımcı programı ( zsh/ perl...) arayın fcntl()).

O Not bashkullanırsanız (çoğu kabukları gibi), (...)yerine {...;}, bu ekstra bir süreç bölünür. Kullanabilirsiniz:

if { true >&3; } 2<> /dev/null

bunun yerine çataldan kaçınmak için (bileşik komutlarının yeniden yönlendirilmesinin her zaman alt kabuğa neden olduğu Bourne kabuğu hariç). Kullanmayın :yerine truebir olduğu gibi özel bash POSIX uyumluluk modunda olduğunda yerleşik, böylece çıkmak için kabuk neden olur.

Ancak şunları kısaltabilirsiniz:

if { >&3; } 2<> /dev/null

@mikeserve, re: düzenleme, bununla ne <>? Kabuk stderr'den okumayacak, neden okuma + yazmada açmak istesin? İçsel olana ne demek istiyorsun ? ?
Stéphane Chazelas

7

POSIX Uygulama Kullanımı açıklamasında aşağıdakileri bulacaksınız:command

Özel yerleşiklerin özel özelliklerini zaman zaman bastırmanın bazı avantajları vardır. Örneğin:

command exec > unwritable-file

etkileşimli olmayan bir komut dosyasının iptal edilmesine neden olmaz, böylece çıktı durumu komut dosyası tarafından kontrol edilebilir.

Bu yüzden şunları yapabilirsiniz:

if    command >&3
then  echo 3 is open >&3
else  ! echo 3 is not open
fi    2<>/dev/null

Veya...

{ command >&3
  printf %s\\n%.0d  string "0$(($??8:0))" >&"$(($??1:3))"
} 2<>/dev/null

Yazacak Hangi dize bir takip \nmatematik üzerinde yapılan çünkü 3 açık değilken sıfırdan farklı çıkış durumu aktarmak ya Stdout'a veya hala 3 ve karşı ewline $?sekizlik dönüştürmek için başarısız kadar rüzgarlara 08 kadar % ondalık sonunda bir hiçe ama gövdelerine sekizli 00 .

Veya...

command exec >&3 || handle_it

Ancak kullanıyorsanız ksh93, şunları yapabilirsiniz:

fds

Açık dosya tanımlayıcılarının listesi için. -lNereye gittiklerini görmek için ekleyin .


3

Açık dosya tanımlayıcıları bulunabilir /proc/<pid>/fd. Örneğin, geçerli kabuğun açık dosya tanımlayıcılarını listelemek için aşağıdakilere ls -l /proc/$$/fdbenzer bir şey vermelisiniz:

total 0
lrwx------ 1 testuser testuser 64 jun  1 09:11 0 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:11 1 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:11 2 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:39 255 -> /dev/pts/3

Bir dosyayı aşağıdakileri kullanarak açtığınızda:

touch /tmp/myfile
exec 7</tmp/myfile

Yeni bir liste ile listelenmelidir ls -l /proc/$$/fd:

lr-x------ 1 testuser testuser 64 jun  1 09:11 7 -> /tmp/myfile

Dosya tanımlayıcıyı kullanarak tekrar kapatırsanız, artık exec 7>&-listelenmez /proc/$$/fd.


2
Bütün bunlar Linux'a oldukça özgüdür. FWIW.
lcd047

1
Linux'ta ve Solaris'te (10 ve 11) test edildi. Aradaki fark, Linux'ta bağlantıyı görüntülerken pfiles <pid>hangi dosya tanımlayıcısının hangi dosyaya bağlı olduğunu görmek için kullanmanızdır ls -l.
Lambert

Kompaktlığını seviyorum [ -e /proc/$$/fd/3 ], ancak FreeBSD'de ve muhtemelen diğer üniversitelerde kullanımdan kaldırıldığı için procfs'ye güvenmemeyi tercih ediyorum.
Witiko

1
Beni hangi dosya tanımlayıcılarının açık olduğunu görme pfiles <pid>veya kullanma alternatifine getiriyor lsof -p <pid>.
Lambert

1
/procOpenBSD'de hiç mevcut değil. FreeBSD ve mountNetBSD'de açıkça belirtilmelidir ve /proc/<PID>bir alt dizine sahip değildir fd.
lcd047

3

Sizin hileniz sevimli görünüyor; ama deyimsel bir şekilde neden kullanmadığınızı merak ediyorum:

if ( exec 1>&3 ) 2>&-

Bu gerçekten daha temiz bir yol.
Witiko

5
Bu, çoğu kabuk olan bir alt kabuk yaratır, bu da bir süreci çatallamak anlamına gelir. Bu, fd'nin yazılabilir olduğunu garanti etmez. { true >&3; } 2> /dev/nullÇatalı önlemek için kullanabilirsiniz . Veya { command exec >&3; } 2> /dev/nullstdout'u ona yeniden yönlendirmek istiyorsanız.
Stéphane Chazelas

@Stephane; @Witiko'nun icat ettiği alt kabuk hilesi, bir yönlendirme elde etmek için bir yönlendirme kullanırken mevcut ortamın dosya tanımlayıcılarını etkilememekti. - Bahsettiğin "yazılabilir fd" üzerinde biraz durur musun?
Janis

2
{ true >&3; } 2> /dev/nullmevcut ortamı da etkilemez ve çatallanmaz (Bourne kabuğu hariç). Yani bu (exec 1>&3) 2>&-salt okunur modda açık bir fd için true değerini döndürür.
Stéphane Chazelas

1
execözel bir yerleşik olmak başarısız olursa kabuktan çıkar (bash için, yalnızca POSIX uyumluluk modundayken). command execbunu engeller. trueözel bir yapı değildir. O Not execve command execgüncel çevreyi nasıl etkiler (yani dedim neden en ona stdout'u yönlendirmek istiyorsanız ).
Stéphane Chazelas

-1

Tekrar tekrar kullanmak için düşük çatallı bir çözümle ilgileniyorsanız, bu işlevi öneririm:

checkfd () {
    exec 2> / dev / null
    exec> & 3 ise; sonra
        exec 1> / dev / tty
        echo "fd3 Tamam"
    Başka
        yankı "fd3 KO"
    fi
    exec 2> / dev / tty
}

Ve işte bir ile ürettiği şey zsh:

$ checkfd            
fd3 KO
$ checkfd 3> / dev / null
fd3 Tamam
$

Çoğu kabuk exec >&33 açık olmadığında kabuğu öldürür.
mikeserv

En azından zshve üzerinde çalışıyor bash. Başarısızlığın execneden olduğu mermiyi sağlayabilir misiniz exit?
dan

Evet. Gelen bashyapmak set -o posixve yeniden deneyin. In zsh... Ben bu env var POSIX_BUILTINSboş bir değere ayarlama meselesi düşünüyorum - ama hazırlıksız unutmak. Her durumda, zshPOSIX uyumluluğunu deneyen bir kabuk değildir ve bu yüzden kesinlikle standart değildir. Bu mermilerin her ikisi de bazılarının rahatlık olduğuna inandığı şeyle uyumluluktan kaçınır .
mikeserv

Ayrıca düz Bourne kabuğu üzerinde çalışıyor.
dan

Bash, set -o posixbir deneme ile başarılı.
dan

-1

Bu çok kolay görünüyor (yorumlara bakın):

[ -r /proc/$$/fd/$FD ] && echo "File descriptor $FD is readable"
[ -w /proc/$$/fd/$FD ] && echo "File descriptor $FD is writable"

Ek olarak ... [-r dosyası] testi, gerçekten okunmayı bekleyen herhangi bir veri olup olmadığını göstermez (/ dev / null bu testi geçer (açıklamalara bakın)).

[ -r /proc/$$/fd/4 ] \
  && [ read -t 0.0001 -N 0 <&4 ] \
  && echo "Data is waiting to be read from file descriptor 4"

Zaman aşımı bağımsız değişkeni (read -t) için küçük bir sayı gerekir veya bazı hesaplama gerektiren veriler gözden kaçabilir. Okunabilir test ([-r dosya]) gereklidir veya dosya okunamıyorsa okuma komutu bombalanır. Bayt sayısı sıfır olduğu için bu aslında hiçbir veri okumaz (read -N 0).


Bir Linux sistemi varsayarsanız, /proc/<pid>/fdinfo/<fd>altındaki tüm açık dosya modunu listeleyen bir göz atabilirsiniz flags:- buraya bakın . Neden 2. bölümünüz (göze çarpan hatayı düzelttikten sonra bile): read -t .1 -N0 <&4fd 4'te okunacak veriler olup olmadığını söylemeyecektir: sadece deneyin 4</dev/null.
mosvy

Ve tabii ki, [ -r /proc/$$/fd/$FD ]dosya tanımlayıcısının $FDokunabilir olup olmadığını söylemez , ancak açık olduğu dosya yeniden okumak için başka bir dosya tanımlayıcıyla tekrar açılabilirse :exec 7>/tmp/foo; [ -r /proc/$$/fd/7 ] && echo fd 7 can be read from && cat <&7
mosvy

-1

Soru oldukça eski - ama yine de - neden sadece yerleşikleri kullanmıyorsunuz?

for i in {0..5} ; do if [ -t $i ]; then echo "$i is a valid FD"; else echo "$i is INVALID FD"; fi; done

Çıktı:

0 is a valid FD
1 is a valid FD
2 is a valid FD
3 is INVALID FD
4 is INVALID FD
5 is INVALID FD

Yani, soruya cevap vermek - şunu önerecektir:

if [ -t 3 ]; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi

-tbir dosya tanımlayıcının geçerli olup olmadığını test etmez, ancak bir tty'ye bağlı olup olmadığını test eder. Senaryonuza a echo yup |ekleyin ve diyelim ki 0 is INVALID FDaslında çok geçerli bir fd, bir pipo.
mosvy
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.