Kiểm tra nếu một mô tả tập tin là hợp lệ


12

Tôi muốn tạo một tập lệnh bash xuất thông tin bổ sung cho bộ mô tả tệp (FD) lớn hơn hoặc bằng 3, khi chúng được mở. Để kiểm tra xem FD có mở không, tôi đã nghĩ ra mẹo sau:

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

Điều này là đủ cho nhu cầu của tôi, nhưng tôi tò mò liệu có cách kiểm tra nào thành ngữ hơn nếu FD là hợp lệ. Tôi đặc biệt quan tâm đến việc có tồn tại ánh xạ của tòa nhà fcntl(1)thành lệnh shell hay không, điều này sẽ cho phép truy xuất các cờ FD ( O_WRONLYO_RDWRđể kiểm tra xem FD có thể ghi được hay không O_RDONLYO_RDWRđể kiểm tra xem FD có đọc được không).

Câu trả lời:


12

Trong ksh(cả hai biến thể AT & T và pdksh) hoặc zsh, bạn có thể làm:

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

Họ sẽ không viết bất cứ điều gì trên fd đó, nhưng vẫn kiểm tra xem fd có thể ghi được không (sử dụng fcntl(3, F_GETFL)) và báo cáo lỗi khác:

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

(mà bạn có thể chuyển hướng đến /dev/null).

Với bash, tôi nghĩ rằng lựa chọn duy nhất của bạn là kiểm tra xem một dup()thành công như trong cách tiếp cận của bạn, mặc dù điều đó sẽ không đảm bảo rằng fd có thể ghi được (hoặc gọi một tiện ích bên ngoài ( zsh/ perl...) để thực hiện fcntl()).

Lưu ý rằng trong bash(giống như hầu hết các shell), nếu bạn sử dụng (...)thay vì {...;}, điều đó sẽ tạo ra một quá trình bổ sung. Bạn có thể dùng:

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

thay vào đó để tránh ngã ba (ngoại trừ trong vỏ Bourne, nơi chuyển hướng các lệnh ghép luôn gây ra một lớp con). Không sử dụng :thay vì trueđó là một nội dung đặc biệt , do đó sẽ khiến trình bao thoát ra khi bash ở chế độ tuân thủ POSIX.

Tuy nhiên, bạn có thể rút ngắn nó thành:

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

@mikeerve, re: chỉnh sửa của bạn, đó là <>gì? Shell sẽ không đọc từ stderr của nó, tại sao bạn muốn mở nó trong read + write? Bạn có ý nghĩa với những gì đã xảy ra với nội tại? ?
Stéphane Chazelas

7

Trong phần mô tả sử dụng ứng dụng POSIX, bạn sẽ tìm thấy những điều sau đây:command

Có một số lợi thế để loại bỏ các đặc tính đặc biệt của các công cụ xây dựng đặc biệt trong dịp này. Ví dụ:

command exec > unwritable-file

không làm cho tập lệnh không tương tác bị hủy bỏ, do đó có thể kiểm tra trạng thái đầu ra của tập lệnh.

Đây là lý do tại sao bạn chỉ có thể làm:

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

Hoặc là...

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

Chuỗi nào sẽ viết chuỗi theo sau là \newline hoặc stdout hoặc 3 và vẫn chuyển sang trạng thái thoát khác không khi 3 không mở vì toán học được thực hiện khi $?không chuyển đổi bát phân 08 thành % thập phân nhưng bị cắt cụt thành không có gì cả bát phân 00 .

Hoặc là...

command exec >&3 || handle_it

Nhưng nếu bạn đang sử dụng ksh93, bạn chỉ có thể làm:

fds

Đối với một danh sách các mô tả tập tin mở. Thêm -lđể xem họ đi đâu.


3

Mô tả tập tin mở có thể được tìm thấy trong /proc/<pid>/fd. Để liệt kê, ví dụ, các mô tả tệp mở của shell hiện tại, bạn có thể đưa ra ls -l /proc/$$/fdcái sẽ cung cấp cho bạn một cái gì đó như:

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

Khi bạn mở tệp bằng:

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

Nó nên được liệt kê bởi một cái mới ls -l /proc/$$/fd:

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

Nếu bạn đóng mô tả tập tin một lần nữa bằng cách sử dụng exec 7>&-nó cũng không được liệt kê trong /proc/$$/fdnữa.


2
Tất cả điều này khá cụ thể đối với Linux. FWIW.
lcd047

1
Đã thử nghiệm nó trên Linux cũng như trên Solaris (10 và 11). Sự khác biệt là bạn cần sử dụng pfiles <pid>để xem bộ mô tả tệp nào được kết nối với tệp nào trong khi ls -lhiển thị kết nối trên Linux.
Lambert

Tôi thích sự gọn nhẹ của [ -e /proc/$$/fd/3 ], nhưng tôi không thích dựa vào Procfs, vì nó không được dùng trong FreeBSD và có thể cả những trường hợp khác.
Witiko

1
Đưa tôi đến phương án thay thế bằng cách sử dụng pfiles <pid>hoặc lsof -p <pid>để xem mô tả tệp nào đang mở.
Lambert

1
/prochoàn toàn không tồn tại trên OpenBSD. Trên FreeBSD và NetBSD, nó phải được xác mountđịnh rõ ràng và /proc/<PID>không có thư mục con fd.
lcd047

3

Thủ thuật của bạn trông dễ thương; nhưng đối với một cách thành ngữ tôi tự hỏi tại sao bạn không sử dụng:

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

Đây thực sự là một cách sạch hơn.
Witiko

5
Điều đó tạo ra một lớp con mặc dù hầu hết các shell có nghĩa là tạo ra một quá trình. Điều đó không đảm bảo fd có thể ghi. Bạn có thể sử dụng { true >&3; } 2> /dev/nullđể tránh ngã ba. Hoặc { command exec >&3; } 2> /dev/nullnếu bạn muốn chuyển hướng thiết bị xuất chuẩn đến nó.
Stéphane Chazelas

@Stephane; Thủ thuật subshell mà @Witiko đã phát minh ra là không ảnh hưởng đến các mô tả tệp của môi trường hiện tại khi sử dụng chuyển hướng để có được chuyển hướng. - Bạn có thể nói rõ hơn về "fd có thể ghi" mà bạn đề cập không?
Janis

2
{ true >&3; } 2> /dev/nullcũng sẽ không ảnh hưởng đến môi trường hiện tại và sẽ không rẽ nhánh (ngoại trừ trong vỏ Bourne). Ý tôi là điều đó (exec 1>&3) 2>&-sẽ trả về true cho một fd mở ở chế độ chỉ đọc.
Stéphane Chazelas

1
execlà một nội dung đặc biệt sẽ thoát khỏi trình bao nếu nó bị lỗi (đối với bash, chỉ khi ở chế độ tuân thủ POSIX). command execngăn chặn điều đó truekhông phải là một nội dung đặc biệt. Lưu ý rằng execcommand execcó ảnh hưởng đến môi trường hiện tại (đó là lý do tại sao tôi đã nói nếu bạn muốn chuyển hướng thiết bị xuất chuẩn sang nó ).
Stéphane Chazelas

-1

Nếu bạn quan tâm đến một giải pháp rèn thấp để sử dụng nó nhiều lần, tôi sẽ đề xuất chức năng này:

kiểm tra () {
    thực hiện 2> / dev / null
    nếu thực hiện> & 3; sau đó
        thực hiện 1> / dev / tty
        tiếng vang "fd3 OK"
    khác
        tiếng vang "fd3 KO"
    fi
    thực hiện 2> / dev / tty
}

Và đây là những gì nó tạo ra với zsh:

$ kiểm tra            
fd3 KO
$ checkfd 3> / dev / null
fd3 OK
$

Trong hầu hết các vỏ exec >&3sẽ giết chết vỏ khi 3 không mở.
mikeerv

Ít nhất là nó đang làm việc zshbash. Bạn có thể cung cấp vỏ mà sự thất bại execgây ra exit?
dan

Vâng. Trong bashlàm set -o posixvà thử lại. Trong zsh... tôi nghĩ đó là vấn đề đặt var env POSIX_BUILTINSthành giá trị không null - nhưng tôi quên mất. Trong mọi trường hợp, zshkhông phải là một vỏ cố gắng tuân thủ POSIX, và do đó, nó chắc chắn là không chuẩn. Cả hai vỏ tương thích eschew cho những gì một số người tin là tiện lợi.
mikeerv

Nó cũng đang làm việc trên vỏ Bourne đơn giản.
dan

Trong bash, với set -o posixmột thử là thành công.
dan

-1

Điều này có vẻ siêu dễ dàng (xem bình luận):

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

Ngoài ra ... Thử nghiệm [-r file] không cho biết liệu có dữ liệu nào thực sự đang chờ để đọc hay không (/ dev / null vượt qua thử nghiệm này (xem bình luận)).

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

Một số số nhỏ cho đối số thời gian chờ (đọc -t) là bắt buộc hoặc dữ liệu cần tính toán có thể bị bỏ sót. Kiểm tra có thể đọc được ([-r file]) là bắt buộc hoặc lệnh đọc sẽ đánh bom nếu không thể đọc được tệp. Điều này thực sự sẽ không đọc bất kỳ dữ liệu nào vì số byte bằng 0 (đọc -N 0).


nếu bạn định sử dụng một hệ thống Linux, bạn cũng có thể xem qua /proc/<pid>/fdinfo/<fd>, trong đó liệt kê tất cả các chế độ tệp đang mở bên dưới flags:- xem tại đây . Vì lý do tại sao phần 2 của bạn (ngay cả sau khi sửa lỗi chói): read -t .1 -N0 <&4sẽ không cho biết liệu có dữ liệu nào được đọc trên fd 4 hay không: chỉ cần thử với 4</dev/null.
mosvy

Và tất nhiên, [ -r /proc/$$/fd/$FD ]không cho bạn biết liệu bộ mô tả tệp $FDcó thể đọc được hay không, nhưng nếu tệp được mở từ đó có thể được mở lại , với một bộ mô tả tệp khác, để đọc:exec 7>/tmp/foo; [ -r /proc/$$/fd/7 ] && echo fd 7 can be read from && cat <&7
mosvy

-1

Câu hỏi khá cũ - nhưng dù sao đi nữa - tại sao không sử dụng nội dung?

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

Đầu ra:

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

Vì vậy, để trả lời câu hỏi - sẽ đề nghị:

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

-tkhông kiểm tra xem một bộ mô tả tập tin có hợp lệ không, nhưng nếu nó được kết nối với một tty. Chuẩn bị một echo yup |tập lệnh của bạn và sẽ nói rằng 0 is INVALID FD, trong khi thực tế nó là fd rất hợp lệ, một đường ống.
mosvy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.