Di Bash, untuk apa deskriptor file 255, bisakah saya menggunakannya?

10

Saya mengerti deskriptor file (atau file handler) adalah teknik file IO dalam sistem Linux.

Saya juga tahu bahwa setiap proses memiliki 3 stream standar (yaitu stdin, stdout dan stderr) yang diwakili oleh file dengan deskriptor dari 0 hingga 3.

Namun, saya perhatikan bahwa semua proses yang saya teliti lsof -p <pid>memiliki deskriptor file tambahan 255dengan izin baca.

Dari jawaban ini , saya mengetahui bahwa fitur ini khusus untuk Bash shell , namun jawaban dan sumber yang direferensikan tidak benar-benar menjelaskan untuk apa deskriptor file ini.

Pertanyaan saya:

  1. Untuk apa 255 deskriptor file?
  2. Bisakah saya memanfaatkannya dalam skrip Bash saya atau hanya mekanisme kerja internal yang tidak seharusnya digunakan / dimanipulasi secara manual?
Tran Triet
sumber
Menurut pendapat saya pertanyaan Anda dijawab pada halaman yang ditautkan.
Cyrus
Saya akan memeriksa jawabannya lagi untuk melihat apakah itu menjawab pertanyaan saya. Saya hanya memperhatikan sekarang bahwa
Tran Triet
2
@Cyrus mengatakan bahwa "ini adalah trik kecil" tanpa menjelaskan apa "trik kecil" itu bukanlah jawaban yang tepat.
Mosvy
Komentar pertama pada jawaban yang ditautkan tampaknya memiliki diskusi yang lebih baik ... Balasan terakhir mungkin adalah apa yang Anda cari ...
RubberStamp

Jawaban:

12

Untuk bagian terakhir dari pertanyaan Anda:

bisakah saya menggunakannya?

Dari man bash:

Pengalihan menggunakan deskriptor file yang lebih besar dari 9 harus digunakan dengan hati-hati, karena dapat bertentangan dengan deskriptor file yang shell gunakan secara internal.

Jadi, jika Anda maksud menggunakan sebagai membuat fd baru dengan nomor itu jawabannya adalah tidak.

Jika maksud Anda gunakan sebagai: "tulis ke fd itu":

$ echo hello >/dev/fd/255"

Atau untuk membacanya:

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

jawabannya iya.
Tetapi, mungkin, itu harus lebih baik (tidak tergantung dari shell) untuk digunakan /dev/ttyuntuk mengakses tty.

untuk apa file descriptor 255?

Sebagai koneksi alternatif ke tty dalam kasus fd 1 ( /dev/stdout) dan fd 0 ( /dev/stdin) diblokir.

Lebih detail .

Kerang lain mungkin menggunakan nomor yang berbeda (seperti 10 dalam 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% 

Dari daftar surat :

Fd 255 digunakan secara internal sebagai koneksi ke tty, sehingga tidak mengganggu penggunaan exec untuk memindahkan fds. Bash juga mengalokasikan fds tinggi saat menangani subtitusi proses `<(foo) ', untuk alasan yang sama.
Andreas Schwab

Ishak
sumber
fd 255 tidak digunakan untuk "menyimpan salinan fd 1 dan fd 0" - Anda dapat dengan mudah memverifikasi itu dengan dd bs=1 | bash -i -c 'sleep .1; ls -l /proc/$$/fd' 2>/tmp/err | tee /tmp/out. Juga, komentar dari milis adalah tentang kapan bashdijalankan sebagai bash scriptfile( 255dalam hal ini pegangan terbuka untuk scriptfile- dan dalam hal itu, ls -l /proc/pid/fdakan mencetak dengan sangat meyakinkan 255 -> scriptfile;-)), bukan tentang kapan itu dijalankan secara interaktif.
Mosvy
Saya minta maaf bahwa cuplikan kode sumber dan analisis dari jawaban saya tidak meyakinkan Anda, tetapi hanya untuk memperjelas: a) ini bukan koneksi "alternatif" ke tty, tetapi koneksi utama ke tty, digunakan untuk semua tty terkait tujuan b) itu disalin dari fd 2 (stderr) atau dibuka langsung dari /dev/tty, bukan dari fd 0 atau fd 1 c) jika fds 0, 1 atau 2 "diblokir", bash tidak akan menggunakan 255 fd sebagai alternatif untuk membaca input dari pengguna atau untuk menulis output perintah, prompt, pesan kesalahan, dll.
mosvy
8

Itu 255file descriptor adalah pegangan terbuka untuk tty mengendalikan dan hanya digunakan ketika bashdijalankan dalam modus interaktif.

Ini memungkinkan Anda untuk mengarahkan ulang stderrdi shell utama, sambil tetap memungkinkan kontrol pekerjaan berfungsi (mis. Dapat mematikan proses dengan ^ C, mengganggu mereka dengan ^ Z, dll).

Contoh:

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

Jika Anda mencobanya di shell like ksh93, yang hanya menggunakan file deskriptor 2 sebagai referensi ke terminal pengendali, sleepprosesnya akan kebal terhadap ^ C dan ^ Z, dan harus terbunuh dari jendela / sesi lain. Itu karena shell tidak akan dapat mengatur grup proses sleepsebagai latar depan di terminal tcsetgrp(), karena file deskriptor 2 tidak lagi menunjuk ke terminal.

Ini tidak bashspesifik, ini juga digunakan dalam dashdan zsh, hanya bahwa deskriptor tidak dipindahkan setinggi itu (biasanya 10).

zsh juga akan menggunakan fd itu untuk mengulang permintaan dan input pengguna, jadi cukup yang berikut ini akan berfungsi:

$ exec 2>/tmp/err
$ 

Ini tidak ada hubungannya dengan menangani file bashmenggunakan ketika membaca skrip dan mengatur pipa (yang juga ditipu keluar dari jalan dengan fungsi yang sama - move_to_high_fd()), seperti yang disarankan dalam jawaban dan komentar lain.

bashmenggunakan jumlah yang sedemikian besar untuk memungkinkan fds lebih besar daripada yang 9digunakan dengan pengalihan in-shell (mis. exec 87<filename); itu tidak didukung di shell lain.

Anda dapat menggunakan file itu menangani sendiri, tetapi ada gunanya melakukannya, karena Anda bisa mendapatkan pegangan ke terminal pengendali yang sama di setiap perintah ... < /dev/tty.

Analisis kode sumber bash :

Dalam bash, deskriptor file dari terminal pengendali disimpan dalam shell_ttyvariabel. Jika shell bersifat interaktif, variabel tersebut diinisialisasi (pada saat startup atau setelah eksekutif yang gagal) masuk jobs.c:initialize_job_control()dengan dup'ing dari stderr(jika stderrterpasang ke terminal) atau dengan langsung membuka /dev/tty, dan kemudian ditiru lagi ke fd yang lebih tinggi dengan 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);
      ...
    }

Jika shell_ttytty belum mengendalikan, maka dibuat demikian:

          /* 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 kemudian digunakan untuk

  1. mendapatkan dan mengatur kelompok proses latar depan dengan tc[sg]etpgrpdi jobs.c:maybe_give_terminal_to(), jobs.c:set_job_control()danjobs.c:give_terminal_to()

  2. dapatkan dan atur termios(3)params di jobs.c:get_tty_state()danjobs.c:set_tty_state()

  3. dapatkan ukuran jendela terminal dengan ioctl(TIOCGWINSZ)in lib/sh/winsize.c:get_new_window_size().

move_to_high_fd()umumnya digunakan dengan semua deskriptor file sementara yang digunakan oleh bash(file skrip, pipa, dll), dari sana kebingungan dalam sebagian besar komentar yang muncul secara jelas dalam pencarian google.

Deskriptor file yang digunakan secara internal oleh bash, termasuk shell_ttysemua diatur ke close-on-exec, sehingga mereka tidak akan bocor ke perintah.

mosvy
sumber