it-swarm-id.com

Kapan Anda akan menggunakan deskriptor file tambahan?

Saya tahu Anda dapat membuat deskriptor file dan mengarahkan ulang hasilnya. misalnya.

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

Tetapi Anda dapat melakukan hal yang sama tanpa deskriptor file:

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

Saya mencari contoh yang baik tentang kapan Anda harus menggunakan deskriptor file tambahan.

80
dogbane

Sebagian besar perintah memiliki saluran input tunggal (input standar, deskriptor file 0) dan saluran output tunggal (output standar, deskriptor file 1) atau beroperasi pada beberapa file yang dibuka sendiri (sehingga Anda memberikan mereka nama file). (Itu selain dari kesalahan standar (fd 2), yang biasanya menyaring semua jalan ke pengguna.) Namun kadang-kadang nyaman untuk memiliki perintah yang bertindak sebagai filter dari beberapa sumber atau ke beberapa target. Misalnya, inilah skrip sederhana yang memisahkan baris bernomor ganjil dalam file dari yang genap bernomor

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

Sekarang anggaplah Anda ingin menerapkan filter yang berbeda ke garis bilangan ganjil dan ke garis genap (tetapi tidak menyatukannya kembali, itu akan menjadi masalah yang berbeda, tidak layak dari Shell pada umumnya). Di Shell, Anda hanya bisa menyalurkan output standar perintah ke perintah lain; untuk mem-pipe deskriptor file lain, Anda harus mengarahkannya ke fd 1 terlebih dahulu.

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

Kasus penggunaan lain yang lebih sederhana adalah memfilter output kesalahan dari sebuah perintah .

exec M>&N mengarahkan kembali deskriptor file ke yang lain untuk sisa skrip (atau sampai perintah lain mengubah deskriptor file lagi). Ada beberapa fungsi yang tumpang tindih antara exec M>&N dan somecommand M>&N. Bentuk exec lebih kuat karena tidak harus disarangkan:

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

Contoh lain yang mungkin menarik:

Dan untuk lebih banyak contoh:

P.S. Ini adalah pertanyaan mengejutkan yang berasal dari penulis posting yang paling dipilih di situs yang menggunakan pengalihan melalui fd !

Berikut adalah contoh menggunakan FD tambahan sebagai kontrol chattiness script bash:

#!/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"
14
Fordi

Dalam konteks pipa bernama (fifos) penggunaan deskriptor file tambahan dapat mengaktifkan perilaku perpipaan yang tidak memblokir.

(
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

Lihat: Pipa Bernama menutup sebelum waktunya dalam skrip?

8
chad

Deskriptor file tambahan baik untuk ketika Anda ingin menangkap stdout dalam variabel namun masih ingin menulis ke layar, misalnya dalam antarmuka pengguna skrip bash

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 
}

Berikut ini skenario lain ketika menggunakan deskriptor file tambahan yang tampaknya sesuai (dalam Bash):

Keamanan kata sandi skrip Shell parameter parameter baris perintah

env -i bash --norc   # clean up environment
set +o history
read -s -p "Enter your password: " passwd
exec 3<<<"$passwd"
mycommand <&3  # cat /dev/stdin in mycommand
3
bernard

Contoh: menggunakan kawanan untuk memaksa skrip dijalankan secara seri dengan kunci file

Salah satu contoh adalah dengan menggunakan penguncian file untuk memaksa skrip untuk menjalankan sistem seri lebar. Ini berguna jika Anda tidak ingin dua skrip dari jenis yang sama beroperasi pada file yang sama. Jika tidak, kedua skrip akan saling mengganggu dan kemungkinan data korup.

#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>&-

Gunakan kawanan secara fungsional dengan mendefinisikan kunci dan membuka kunci

Anda juga dapat menggunakan logika penguncian/penguncian ini menjadi fungsi yang dapat digunakan kembali. Yang berikut trap Shell builtin akan secara otomatis melepaskan kunci file ketika skrip keluar (baik kesalahan atau sukses). trap membantu membersihkan kunci file Anda. Jalan /tmp/file.lock harus berupa jalur kode yang keras sehingga banyak skrip dapat mencoba untuk menguncinya.

# 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>&-
}

Logika unlock di atas adalah untuk menghapus file sebelum kunci dilepaskan. Dengan cara ini membersihkan file kunci. Karena file itu dihapus, contoh lain dari program ini dapat memperoleh kunci file.

Penggunaan fungsi kunci dan buka kunci dalam skrip

Anda dapat menggunakannya dalam skrip Anda seperti contoh berikut.

#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

Jika Anda ingin kode Anda menunggu hingga dapat mengunci, Anda dapat menyesuaikan skrip seperti:

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
1
Sam Gleske

Sebagai contoh konkret, saya hanya menulis sebuah skrip yang memerlukan informasi waktu dari sebuah sub-perintah. Menggunakan deskriptor file tambahan memungkinkan saya untuk menangkap stderr perintah time tanpa mengganggu stdout atau stderr subcommand.

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

Apa yang dilakukannya adalah mengarahkan stderr ls ke fd 3, mengarahkan fd 3 ke stderr skrip, dan mengarahkan stderr time ke file. Ketika skrip dijalankan, stdout dan stderr-nya sama dengan sub-perintah, yang dapat dialihkan seperti biasa. Hanya keluaran time yang dialihkan ke file.

$ 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
1
Ben Blank