Proses ikamesi elde etmenin portatif (POSIX) yolu nedir?


25

Bazı kabuklar, işlem çıktısını bir dosya gibi sunmanın bir yolu olan İşlem Değiştirme'yibash destekler :

$ diff <(sort file1) <(sort file2)

Bununla birlikte, bu yapı POSIX değildir ve bu nedenle taşınabilir değildir. Proses ikamesi POSIX dostu bir şekilde nasıl yapılır (yani hangisinde çalışır /bin/sh) ?

not: soru iki sıralı dosyanın nasıl dağıtılacağını sormuyor - bu sadece işlem değişimini göstermek için atfedilen bir örnek !




1
POSIX /bin/shbir POSIX kabuğu gerektirmez , yalnızca shdoğru ortamda POSIX uyumlu olan bir yerde bir komut olması gerekir . Örneğin, Solaris 10'da /bin/shbir POSIX kabuğu değil, eski bir Bourne kabuğu ve POSIX kabuğu içeride /usr/xpg4/bin/sh.
Stéphane Chazelas

Yanıtlar:


17

Bu özellik ksh(ilk olarak ksh86'da belgelenmiştir) tarafından tanıtılmıştır ve /dev/fd/nözelliği kullanmıştır ( daha önce bazı BSD'lerde ve AT&T sistemlerinde bağımsız olarak eklenmiştir). Gelen kshve ksh93u kadar, bu iş için sistem desteği için / dev / fd / n vardı sürece olmaz. zsh, bash ve ksh93u+ve üstü / dev / fd / n'nin bulunmadığı geçici adlandırılmış boruları (inanıyorum SysIII'de eklenen isimli borular) kullanabilirler.

Mevcut olan sistemlerde (POSIX bunları belirtmez), işlem değiştirme işlemini kendiniz ( ) ile yapabilirsiniz:/dev/fd/ndiff <(cmd1) <(cmd2)

{
  cmd1 4<&- | {
    # in here fd 3 points to the reading end of the pipe
    # from cmd1, while fd 0 has been restored from the original
    # stdin (saved on fd 4, now closed as no longer needed)

    cmd2 3<&- | diff /dev/fd/3 -

  } 3<&0 <&4 4<&- # restore the original stdin for cmd2

} 4<&0 # save a copy of stdin for cmd2

Ancak bu ksh93orada Linux ile çalışmaz , kabuk borular boru yerine /dev/fd/3soket çiftleri ile uygulanır ve fd 3 noktalarının bir sokette Linux üzerinde çalışmadığı yerler açılır .

POSIX belirtmese de . Adlandırılmış yöneltmeler belirtir. Adlandırılmış borular, dosya sisteminden erişebilmeniz dışında normal borular gibi çalışır. Buradaki sorun, geçici olanları oluşturmanız ve daha sonra temizlemeniz gerektiğidir; bu, özellikle POSIX'in geçici dosyalar veya dizinler oluşturmak ve taşınabilir bir şekilde sinyal işleme yapmak için standart bir mekanizma ( bazı sistemlerde bulunanlar gibi) olmadığını göz önünde bulundurarak güvenilir bir şekilde yapmak zordur. (telefonu kapattıktan veya öldürdükten sonra temizlemek) de taşınabilir olarak yapmak zordur./dev/fd/nmktemp -d

Gibi bir şey yapabilirsin:

tmpfifo() (
  n=0
  until
    fifo=$1.$$.$n
    mkfifo -m 600 -- "$fifo" 2> /dev/null
  do
    n=$((n + 1))
    # give up after 20 attempts as it could be a permanent condition
    # that prevents us from creating fifos. You'd need to raise that
    # limit if you intend to create (and use at the same time)
    # more than 20 fifos in your script
    [ "$n" -lt 20 ] || exit 1
  done
  printf '%s\n' "$fifo"
)

cleanup() { rm -f -- "$fifo"; }
fifo=$(tmpfifo /tmp/fifo) || exit

cmd2 > "$fifo" & cmd1 | diff - "$fifo"
rm -f -- "$fifo"

(burada sinyal işleme ile ilgilenmiyor).


Adlandırılmış yöneltme örneği bana tamamen açık (sanırım 10 isteğe bağlı bir sınırlama?) Ancak /dev/fd/nörneğinizi çözemiyorum. Tanımlayıcı 4 neden iki kez kapatılıyor? (Ve tanımlayıcı 3 da öyle.) Kayboldum.
Joker

@Wildcard, ihtiyacı olmayan komutlar için kapalı. Burada, 4 fd 3. Yok ihtiyacını fd sadece fark ihtiyaçları, sadece orijinal Stdin yaymak için kullanılan cmd2( dup2(0,4)dış üzerinde {...}restore, dup2(4,0)iç için {...}).
Stéphane Chazelas 14:16

Sen kullanabilirsiniz mktemp -dkimse yepyeni rastgele Temp klasörüne yazma edilmelidir olarak, FIFOları alabilirsiniz sizi temin yardımına.
Daniel H,

@DanielH, zaten bahsetmiştim mktemp -d. Ancak bu standart / POSIX komutu değildir.
Stéphane Chazelas,

Huh, bunun farkında değildim. Hata. Hızlı arama, çoğu sistemin desteklediğini gösteriyor, bu yüzden hala taşınabilir olabilir, ancak POSIX değil.
Daniel H

-1

Kayıp değişkenlerden kaçınmak gerekirse cmd | while read A B C, bunun yerine faydalı :

VAR="before"
while read A B C 
do
  VAR="$A $VAR"
done < <(cmd)
echo "$VAR"

kullanabilirsiniz:

VAR="before"
while read A B C 
do
  VAR="$A $VAR"
done << EndOfText
`cmd`
EndOfText
echo "$VAR"

Yani soruyu cevaplamak için:

sort file1 | diff /dev/stdin /dev/stdout 2<<EOT
`sort file2`
EOT
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.