Ne zaman ek bir dosya tanımlayıcı kullanırsınız?


74

Bir dosya tanımlayıcısı oluşturabileceğinizi ve çıktılara yönlendirilebileceğini biliyorum. Örneğin

exec 3<> /tmp/foo # open fd 3.
echo a >&3 # write to it
exec 3>&- # close fd 3.

Ancak aynı şeyi dosya tanıtıcısı olmadan da yapabilirsiniz:

FILE=/tmp/foo
echo a > "$FILE"

Ne zaman ek bir dosya tanımlayıcı kullanmanız gerektiğine dair iyi bir örnek arıyorum.

Yanıtlar:


50

Çoğu komutun tek bir giriş kanalı (standart giriş, dosya tanımlayıcısı 0) ve tek bir çıkış kanalı (standart çıkış, dosya tanımlayıcısı 1) veya başka bir deyişle kendileri tarafından açtıkları birkaç dosya üzerinde çalışırlar (böylece bir dosya adı iletirsiniz). (Bu, genellikle kullanıcıyı tamamen filtreleyen standart hataya (fd 2) ek olarak gelir.) Ancak, bazen birkaç kaynaktan veya birkaç hedefe filtre görevi gören bir komutun olması uygundur. Örneğin, bir dosyadaki tek numaralı satırları çift numaralı satırlardan ayıran basit bir komut dosyası

while IFS= read -r line; do
  printf '%s\n' "$line"
  if IFS= read -r line; then printf '%s\n' "$line" >&3; fi
done >odd.txt 3>even.txt

Şimdi, tek sayılı satırlara ve çift numaralı satırlara farklı bir filtre uygulamak istediğinizi varsayalım (ancak bunları bir araya getirmeyin, bu genel olarak kabuktan mümkün olmayan farklı bir sorun olur). Kabukta, bir komutun standart çıktısını yalnızca başka bir komuta iletebilirsiniz; başka bir dosya tanımlayıcısını yayınlamak için önce onu fd 1'e yönlendirmeniz gerekir.

{ while  done | odd-filter >filtered-odd.txt; } 3>&1 | even-filter >filtered-even.txt

Bir diğer basit kullanım durumu bir komutun hata çıktısını filtrelemektir .

exec M>&Nbetiğin geri kalanında bir dosya tanımlayıcısını diğerine yönlendirir (ya da başka bir komut dosya tanımlayıcılarını yeniden değiştirinceye kadar). Arasındaki işlevsellik açısından bazı örtüşme vardır exec M>&Nve somecommand M>&N. execFormu iç içe olmak zorunda olmadığını daha güçlüdür:

exec 8<&0 9>&1
exec >output12
command1
exec <input23
command2
exec >&9
command3
exec <&8

İlgi çekici olabilecek diğer örnekler:

Ve daha da fazla örnek için:

PS Bu fd 3 üzerinden yönlendirme kullanan sitedeki en çok oy alan yazının yazarından gelen şaşırtıcı bir soru !


"Çoğu komutun tek veya çift çıkış kanalı vardır - stdout (fd 1) ve çok sık stderr (fd 2)".
rozcietrzewiacz

Ayrıca, neden kullandığınızı da açıklayabilir misiniz while IFS= read -r line;? Gördüğüm gibi, IFS'nin burada bir etkisi yoktur, çünkü sadece bir değişkene ( çizgi ) değer atarsınız . Bu soruya bakınız.
rozcietrzewiacz

@ rozcietrzewiacz Ben stderr'den bahsettim ve IFStek bir değişkene okuyorsanız bile neden bir fark yarattığı için cevabımın ilk bölümünü görün ( önde gelen boşlukları korumak için).
Gilles,

Aynı şeyi yapamaz sed -ne 'w odd.txt' -e 'n;w even.txt'mısın?
Joker

1
@Wildcard Tabii diğer araçlarla da aynısını yapabilirsiniz. Ancak bu cevabın amacı, kabuktaki yönlendirmeleri göstermekti.
Gilles

13

İşte bash script chattiness denetimi olarak fazladan FD kullanmanın bir örneği:

#!/bin/bash

log() {
    echo $* >&3
}
info() {
    echo $* >&4
}
err() {
    echo $* >&2
}
debug() {
    echo $* >&5
}

VERBOSE=1

while [[ $# -gt 0 ]]; do
    ARG=$1
    shift
    case $ARG in
        "-vv")
            VERBOSE=3
        ;;
        "-v")
            VERBOSE=2
        ;;
        "-q")
            VERBOSE=0
        ;;
        # More flags
        *)
        echo -n
        # Linear args
        ;;
    esac
done

for i in 1 2 3; do
    fd=$(expr 2 + $i)
    if [[ $VERBOSE -ge $i ]]; then
        eval "exec $fd>&1"
    else
        eval "exec $fd> /dev/null"
    fi
done

err "This will _always_ show up."
log "This is normally displayed, but can be prevented with -q"
info "This will only show up if -v is passed"
debug "This will show up for -vv"

8

Adlandırılmış borular (fifos) bağlamında, ek bir dosya tanıtıcısının kullanılması, blokaj yapılmayan borulama davranışını sağlayabilir.

(
rm -f fifo
mkfifo fifo
exec 3<fifo   # open fifo for reading
trap "exit" 1 2 3 15
exec cat fifo | nl
) &
bpid=$!

(
exec 3>fifo  # open fifo for writing
trap "exit" 1 2 3 15
while true;
do
    echo "blah" > fifo
done
)
#kill -TERM $bpid

Bkz: Adlandırılmış Kanal komut dosyasında zamanından önce kapanıyor mu?


1
eski sorularımdan birini kazandınız :) chad haklı, bir yarış şartına gireceksiniz.
n0pe

6

Ek bir dosya tanımlayıcısı stdout'u bir değişkende yakalamak istediğinizde, yine de ekrana, örneğin bir bash betiği kullanıcı arayüzünde yazmak istediğinizde kullanışlıdır.

arg1 string to echo 
arg2 flag 0,1 print or not print to 3rd fd stdout descriptor   
function ecko3 {  
if [ "$2" -eq 1 ]; then 
    exec 3>$(tty) 
    echo -en "$1" | tee >(cat - >&3)
    exec 3>&- 
else 
    echo -en "$1"  
fi 
}

2
Bunun yeni bir cevap olmadığını biliyorum, ama ne olduğunu görmek için bir süre kullanmam gerekti ve birinin kullanılmakta olan bu fonksiyonun bir örneğini eklemesi yararlı olacağını düşündüm. Bu bir eko ve tüm çıktısını alır. bir komut - df, bu durumda. .dropboxusercontent.com/u/54584985/mytest_redirect
Joe,


1

Örnek: komut dosyalarını seri kilitlerle dosya kilitleriyle çalıştırmaya zorlamak için sürü kullanmak

Bunun bir örneği, komut dizilerini seri olarak sistem genelinde çalışmaya zorlamak için dosya kilitlemeden yararlanmaktır. Bu, aynı türden iki komut dosyasının aynı dosyalar üzerinde çalışmasını istemiyorsanız kullanışlıdır. Aksi takdirde, iki komut dosyası birbiriyle karışır ve muhtemelen bozuk verilerle sonuçlanır.

#exit if any command returns a non-zero exit code (like flock when it fails to lock)
set -e

#open file descriptor 3 for writing
exec 3> /tmp/file.lock

#create an exclusive lock on the file using file descriptor 3
#exit if lock could not be obtained
flock -n 3

#execute serial code

#remove the file while the lock is still obtained
rm -f /tmp/file.lock

#close the open file handle which releases the file lock and disk space
exec 3>&-

Kilit ve kilidi açarak işlevsel olarak sürüyü kullanın.

Bu kilitleme / açma mantığını tekrar kullanılabilir fonksiyonlara sarabilirsiniz. Aşağıdaki trapkabuk yerleşik kodu , komut dosyası çıktıktan sonra otomatik olarak dosya kilidini açar (hata veya başarı). trapdosya kilitlerini temizlemeye yardımcı olur. Yol /tmp/file.lock, kodlanmış bir yol olmalıdır, böylece birden fazla komut dosyası kilitlemeye çalışabilir.

# obtain a file lock and automatically unlock it when the script exits
function lock() {
  exec 3> /tmp/file.lock
  flock -n 3 && trap unlock EXIT
}

# release the file lock so another program can obtain the lock
function unlock() {
  # only delete if the file descriptor 3 is open
  if { >&3 ; } &> /dev/null; then
    rm -f /tmp/file.lock
  fi
  #close the file handle which releases the file lock
  exec 3>&-
}

Yukarıdaki unlockmantık, kilit serbest bırakılmadan önce dosyayı silmektir. Bu sayede kilit dosyasını temizler. Dosya silindiğinden, bu programın başka bir örneği dosya kilidini elde edebilir.

Kodlarda kilitleme ve kilit açma fonksiyonlarının kullanımı

Aşağıdaki örnekte olduğu gibi komut dosyalarınızda kullanabilirsiniz.

#exit if any command returns a non-zero exit code (like flock when it fails to lock)
set -e

#try to lock (else exit because of non-zero exit code)
lock

#system-wide serial locked code

unlock

#non-serial code

Kodunuzun kilitleninceye kadar beklemesini istiyorsanız, komut dosyasını aşağıdaki gibi ayarlayabilirsiniz:

set -e

#wait for lock to be successfully obtained
while ! lock 2> /dev/null; do
  sleep .1
done

#system-wide serial locked code

unlock

#non-serial code

0

Somut bir örnek olarak, ben sadece bir alt komuttan zamanlama bilgisine ihtiyaç duyan bir senaryo yazdım. Ekstra bir dosya tanımlayıcı kullanmak time, alt komutun stdout veya stderr'sini kesmeden komutun stderr'ını yakalamama izin verdi .

(time ls -9 2>&3) 3>&2 2> time.txt

Bunun anlamı lsstderr'den fd 3'e, fd 3'ü betiğin stderr'ine ve timestderr'i bir dosyaya işaret etmektir. Komut dosyası çalıştırıldığında stdout ve stderr, her zamanki gibi yeniden yönlendirilebilen alt komutlarınkiyle aynıdır. Sadece timeçıkışlar dosyaya yönlendirilir.

$ echo '(time ls my-example-script.sh missing-file 2>&3) 3>&2 2> time.txt' > my-example-script.sh
$ chmod +x my-example-script.sh 
$ ./my-example-script.sh 
ls: missing-file: No such file or directory
my-example-script.sh
$ ./my-example-script.sh > /dev/null
ls: missing-file: No such file or directory
$ ./my-example-script.sh 2> /dev/null
my-example-script.sh
$ cat time.txt

real    0m0.002s
user    0m0.001s
sys 0m0.001s
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.