it-swarm-vi.com

Khi nào bạn sẽ sử dụng một mô tả tập tin bổ sung?

Tôi biết bạn có thể tạo một mô tả tập tin và chuyển hướng đầu ra cho nó. ví dụ.

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

Nhưng bạn có thể làm điều tương tự mà không cần mô tả tệp:

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

Tôi đang tìm kiếm một ví dụ tốt về thời điểm bạn sẽ phải sử dụng một bộ mô tả tệp bổ sung.

80
dogbane

Hầu hết các lệnh đều có một kênh đầu vào (đầu vào tiêu chuẩn, mô tả tệp 0) và một kênh đầu ra duy nhất (đầu ra tiêu chuẩn, mô tả tệp 1) hoặc hoạt động trên một số tệp mà chúng tự mở (vì vậy bạn chuyển cho chúng một tên tệp). (Đó là ngoài lỗi tiêu chuẩn (fd 2), thường lọc toàn bộ cho người dùng.) Tuy nhiên, đôi khi có một lệnh hoạt động như một bộ lọc từ một số nguồn hoặc đến một số mục tiêu. Ví dụ: đây là một tập lệnh đơn giản tách các dòng được đánh số lẻ trong một tệp khỏi các tập lệnh được đánh số chẵn

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

Bây giờ, giả sử bạn muốn áp dụng một bộ lọc khác cho các dòng số lẻ và các dòng có số chẵn (nhưng không đặt chúng lại với nhau, đó sẽ là một vấn đề khác, không khả thi với Shell nói chung). Trong Shell, bạn chỉ có thể chuyển đầu ra tiêu chuẩn của lệnh sang lệnh khác; để mô tả tập tin khác, trước tiên bạn cần chuyển hướng nó đến fd 1.

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

Một trường hợp sử dụng đơn giản hơn là lọc đầu ra lỗi của lệnh .

exec M>&N chuyển hướng một bộ mô tả tệp sang một bộ mô tả khác cho phần còn lại của tập lệnh (hoặc cho đến khi một lệnh khác như vậy thay đổi bộ mô tả tệp một lần nữa). Có một số sự chồng chéo về chức năng giữa exec M>&Nsomecommand M>&N. Biểu mẫu exec mạnh hơn ở chỗ nó không phải lồng nhau:

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

Các ví dụ khác có thể được quan tâm:

Và cho nhiều ví dụ hơn:

P.S. Đây là một câu hỏi đáng ngạc nhiên đến từ tác giả của bài đăng được đánh giá cao nhất trên trang web sử dụng chuyển hướng qua fd !

Dưới đây là một ví dụ về việc sử dụng các FD bổ sung làm kiểm soát độ chói của tập lệnh 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

Trong ngữ cảnh của các đường ống được đặt tên (fifos), việc sử dụng một bộ mô tả tệp bổ sung có thể cho phép hành vi đường ống không chặn.

(
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

Xem: Đặt tên ống đóng sớm trong tập lệnh?

8
chad

Một bộ mô tả tệp bổ sung phù hợp khi bạn muốn bắt đầu xuất chuẩn trong một biến nhưng vẫn muốn ghi ra màn hình, ví dụ như trong giao diện người dùng bash script

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 
}
6

Đây là một kịch bản khác khi sử dụng một bộ mô tả tệp bổ sung có vẻ phù hợp (trong Bash):

Bảo mật mật khẩu tập lệnh Shell của các tham số dòng lệnh

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

Ví dụ: sử dụng bầy để buộc các tập lệnh chạy ser seri với khóa tập tin

Một ví dụ là sử dụng tính năng khóa tệp để buộc các tập lệnh chạy toàn hệ thống. Điều này hữu ích nếu bạn không muốn hai tập lệnh cùng loại hoạt động trên cùng một tệp. Nếu không, hai tập lệnh sẽ can thiệp lẫn nhau và có thể dữ liệu bị hỏng.

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

Sử dụng đàn theo chức năng bằng cách xác định khóa và mở khóa

Bạn cũng có thể bọc logic khóa/mở khóa này thành các chức năng có thể sử dụng lại. Sau đây trap Shell dựng sẵn sẽ tự động giải phóng khóa tệp khi tập lệnh thoát (lỗi hoặc thành công). trap giúp dọn sạch các khóa tệp của bạn. Con đường /tmp/file.lock nên là một đường dẫn được mã hóa cứng để nhiều tập lệnh có thể cố gắng khóa trên nó.

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

Logic unlock ở trên là xóa tệp trước khi khóa được giải phóng. Bằng cách này, nó làm sạch các tập tin khóa. Vì tệp đã bị xóa, một phiên bản khác của chương trình này có thể có được khóa tệp.

Sử dụng các chức năng khóa và mở khóa trong các tập lệnh

Bạn có thể sử dụng nó trong các kịch bản của bạn như ví dụ sau đây.

#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

Nếu bạn muốn mã của mình đợi cho đến khi có thể khóa, bạn có thể điều chỉnh tập lệnh như sau:

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

Để làm ví dụ cụ thể, tôi chỉ viết một kịch bản cần thông tin về thời gian từ một tiểu ban. Sử dụng một bộ mô tả tệp bổ sung cho phép tôi chụp được lệnh time stderr mà không làm gián đoạn thiết bị xuất chuẩn hoặc thiết bị xuất chuẩn của tiểu ban.

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

Điều này thực hiện là điểm ls 'stderr đến fd 3, điểm fd 3 tới stderr của tập lệnh và điểm time' stderr vào một tệp. Khi tập lệnh được chạy, thiết bị xuất chuẩn và thiết bị xuất chuẩn của nó giống như của các tiểu ban, có thể được chuyển hướng như bình thường. Chỉ đầu ra của time được chuyển hướng đến tệp.

$ 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