Trong Bash, mô tả tệp 255 là gì, tôi có thể sử dụng nó không?


10

Tôi hiểu mô tả tệp (hoặc trình xử lý tệp) là một kỹ thuật IO tệp trong các hệ thống Linux.

Tôi cũng biết rằng mỗi quy trình có 3 luồng tiêu chuẩn (cụ thể là stdin, stdout và stderr) được thể hiện bằng các tệp có mô tả từ 0 đến 3.

Tuy nhiên, tôi nhận thấy rằng tất cả các quy trình tôi đã kiểm tra lsof -p <pid>có một mô tả tệp bổ sung 255với quyền đọc.

Từ câu trả lời này , tôi đã biết rằng tính năng này là dành riêng cho shell Bash , tuy nhiên cả câu trả lời và nguồn được tham chiếu không thực sự giải thích được bộ mô tả tệp này dùng để làm gì.

Câu hỏi của tôi:

  1. Bộ mô tả tệp 255 để làm gì?
  2. Tôi có thể sử dụng nó trong tập lệnh Bash của mình không hay nó chỉ là một cơ chế làm việc nội bộ không được sử dụng / thao tác bằng tay?

Theo tôi câu hỏi của bạn đã được trả lời trên trang được liên kết.
Cyrus

Tôi sẽ kiểm tra câu trả lời một lần nữa để xem nếu nó trả lời câu hỏi của tôi. Bây giờ tôi chỉ nhận thấy rằng bạn là người đã đưa ra câu trả lời đó;)
Trần Trừng

2
@Cyrus nói rằng "đó là một mẹo nhỏ" mà không giải thích "trò lừa nhỏ" đó không phải là một câu trả lời thích hợp.
mosvy

Nhận xét đầu tiên về câu trả lời được liên kết dường như có một cuộc thảo luận tốt hơn ... Câu trả lời cuối cùng có lẽ là thứ bạn đang tìm kiếm ...
RubberStamp

Câu trả lời:


12

Đối với phần cuối cùng của câu hỏi của bạn:

tôi có thể sử dụng nó?

Từ man bash:

Các chuyển hướng sử dụng các bộ mô tả tệp lớn hơn 9 nên được sử dụng cẩn thận, vì chúng có thể xung đột với các bộ mô tả tệp mà trình bao sử dụng bên trong.

Vì vậy, nếu bạn có nghĩa là sử dụng như tạo một fd mới với số đó thì câu trả lời là không.

Nếu bạn có nghĩa là sử dụng như: "ghi vào fd đó":

$ echo hello >/dev/fd/255"

Hoặc để đọc từ nó:

$ read a </dev/fd/255
abc
$ echo "$a"
abc

Câu trả lời là có.
Nhưng, có lẽ, nó nên tốt hơn (độc lập với shell) để sử dụng /dev/ttyđể truy cập tty.

Mô tả tập tin 255 để làm gì?

Là một kết nối thay thế cho tty trong trường hợp fd 1 ( /dev/stdout) và fd 0 ( /dev/stdin) bị chặn.

Chi tiết hơn .

Các shell khác có thể sử dụng một số khác (như 10 trong zsh)

$ zsh
mail% ls -l /proc/self/fd /proc/$$/fd/* &
[1] 3345
mail% lrwx------ 1 isaac isaac 64 Oct 14 09:46 /proc/3250/fd/0 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 /proc/3250/fd/1 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 /proc/3250/fd/10 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 /proc/3250/fd/2 -> /dev/pts/2

/proc/self/fd:
total 0
lrwx------ 1 isaac isaac 64 Oct 14 09:50 0 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 1 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 2 -> /dev/pts/2
lr-x------ 1 isaac isaac 64 Oct 14 09:50 3 -> /proc/3345/fd

[1]  + done       ls -l /proc/self/fd /proc/$$/fd/*
mail% 

Từ danh sách thư :

Fd 255 được sử dụng nội bộ như một kết nối với tty, do đó nó không can thiệp vào việc sử dụng exec để di chuyển fds. Bash cũng phân bổ fds cao khi xử lý thay thế quá trình `<(foo) ', với lý do tương tự.
Andreas Schwab


fd 255 không được sử dụng để "giữ một bản sao của fd 1 và fd 0" - bạn có thể dễ dàng xác minh điều đó với dd bs=1 | bash -i -c 'sleep .1; ls -l /proc/$$/fd' 2>/tmp/err | tee /tmp/out. Ngoài ra, nhận xét đó từ danh sách gửi thư là về khi nào bashđược chạy dưới dạng bash scriptfile( 255trong trường hợp đó là tay cầm mở tới scriptfile- và trong trường hợp đó, ls -l /proc/pid/fdsẽ in rất thuyết phục 255 -> scriptfile;-)), chứ không phải khi nó chạy tương tác.
mosvy

Tôi xin lỗi vì các đoạn mã và phân tích từ câu trả lời của tôi không thuyết phục được bạn, nhưng chỉ rõ ràng: a) nó không phải là một kết nối "thay thế" cho tty, mà là kết nối chính cho tty, được sử dụng cho tất cả Các mục đích liên quan đến b) nó được sao chép từ fd 2 (stderr) hoặc được mở trực tiếp từ /dev/tty, không phải từ fd 0 hoặc fd 1 c) nếu fds 0, 1 hoặc 2 bị "chặn", bash sẽ không sử dụng 255 fd đó để thay thế đọc đầu vào từ người dùng hoặc để viết đầu ra lệnh, lời nhắc, thông báo lỗi, v.v.
mosvy 17/10/18

8

Bộ 255mô tả tệp đó là một điều khiển mở cho tty kiểm soát và chỉ được sử dụng khi bashđược chạy trong chế độ tương tác.

Nó cho phép bạn chuyển hướng stderrtrong vỏ chính, trong khi vẫn cho phép điều khiển công việc hoạt động (nghĩa là có thể tiêu diệt các tiến trình với ^ C, làm gián đoạn chúng bằng ^ Z, v.v.).

Thí dụ:

$ exec 2> >(tee /tmp/err); ls /nosuchfile; sleep 1000

Nếu bạn thử trong shell như ksh93, chỉ đơn giản là sử dụng mô tả tệp 2 làm tham chiếu đến thiết bị đầu cuối kiểm soát, sleepquá trình sẽ trở nên miễn nhiễm với ^ C và ^ Z, và sẽ phải bị giết từ một cửa sổ / phiên khác. Đó là bởi vì shell sẽ không thể đặt nhóm quy trình sleeplàm tiền cảnh trong thiết bị đầu cuối tcsetgrp(), vì mô tả tệp 2 không còn trỏ đến thiết bị đầu cuối.

Điều này không bashcụ thể, nó cũng được sử dụng trong dashzsh, chỉ có điều là bộ mô tả không được di chuyển quá cao (thường là 10).

zsh cũng sẽ sử dụng fd đó để lặp lại lời nhắc và nhập liệu của người dùng, vì vậy, đơn giản như sau sẽ hoạt động:

$ exec 2>/tmp/err
$ 

Nó không liên quan gì đến việc xử lý tệp bashđang sử dụng khi đọc tập lệnh và thiết lập các đường ống (cũng bị chặn ngoài cùng một chức năng - move_to_high_fd()), như đã được đề xuất trong các câu trả lời và nhận xét khác.

bashđang sử dụng một số lượng lớn như vậy để cho phép fds lớn hơn 9được sử dụng với các chuyển hướng trong vỏ (ví dụ. exec 87<filename); Điều đó không được hỗ trợ trong các shell khác.

Bạn có thể sử dụng tệp đó tự xử lý, nhưng có rất ít điểm khi làm như vậy, bởi vì bạn có thể có một điều khiển đến cùng một thiết bị đầu cuối kiểm soát trong bất kỳ lệnh nào với ... < /dev/tty.

Phân tích mã nguồn của bash :

Trong bash, mô tả tập tin của thiết bị đầu cuối kiểm soát được lưu trữ trong shell_ttybiến. Nếu shell tương tác, biến đó được khởi tạo (khi khởi động hoặc sau khi thực thi không thành công) jobs.c:initialize_job_control()bằng cách nhân đôi nó từ stderr(nếu stderrđược gắn vào thiết bị đầu cuối) hoặc bằng cách mở trực tiếp /dev/tty, và sau đó được nhân đôi lại với fd cao hơn với general.c:move_to_high_fd():

int
initialize_job_control (force)
     int force;
{
  ...
  if (interactive == 0 && force == 0)
    {
      ...
    }
  else
    {
      shell_tty = -1;

      /* If forced_interactive is set, we skip the normal check that stderr
         is attached to a tty, so we need to check here.  If it's not, we
         need to see whether we have a controlling tty by opening /dev/tty,
         since trying to use job control tty pgrp manipulations on a non-tty
         is going to fail. */
      if (forced_interactive && isatty (fileno (stderr)) == 0)
        shell_tty = open ("/dev/tty", O_RDWR|O_NONBLOCK);

      /* Get our controlling terminal.  If job_control is set, or
         interactive is set, then this is an interactive shell no
         matter where fd 2 is directed. */
      if (shell_tty == -1)
        shell_tty = dup (fileno (stderr));        /* fd 2 */

      if (shell_tty != -1)
        shell_tty = move_to_high_fd (shell_tty, 1, -1);
      ...
    }

Nếu shell_ttychưa phải là tty kiểm soát, thì nó được thực hiện như vậy:

          /* If (and only if) we just set our process group to our pid,
             thereby becoming a process group leader, and the terminal
             is not in the same process group as our (new) process group,
             then set the terminal's process group to our (new) process
             group.  If that fails, set our process group back to what it
             was originally (so we can still read from the terminal) and
             turn off job control.  */
          if (shell_pgrp != original_pgrp && shell_pgrp != terminal_pgrp)
            {
              if (give_terminal_to (shell_pgrp, 0) < 0)

shell_tty sau đó được sử dụng để

  1. nhận được và thiết lập các nhóm quá trình foreground với tc[sg]etpgrptrong jobs.c:maybe_give_terminal_to(), jobs.c:set_job_control()jobs.c:give_terminal_to()

  2. nhận và thiết lập các thông số termios(3)trong jobs.c:get_tty_state()jobs.c:set_tty_state()

  3. có được kích thước cửa sổ terminal với ioctl(TIOCGWINSZ)trong lib/sh/winsize.c:get_new_window_size().

move_to_high_fd()thường được sử dụng với tất cả các mô tả tệp tạm thời được sử dụng bởi bash(tệp script, ống dẫn, v.v.), từ đó gây nhầm lẫn trong hầu hết các bình luận xuất hiện nổi bật trong các tìm kiếm của google.

Các bộ mô tả tệp được sử dụng bên trong bash, bao gồm shell_ttytất cả được đặt thành close-on-exec, vì vậy chúng sẽ không bị rò rỉ cho các lệnh.

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.