Wofür kann ich in Bash den Dateideskriptor 255 verwenden?

10

Ich verstehe, dass File Descriptor (oder File Handler) eine Datei-E / A-Technik in Linux-Systemen ist.

Ich weiß auch, dass jeder Prozess 3 Standard-Streams hat (nämlich stdin, stdout und stderr), die durch Dateien mit Deskriptoren von 0 bis 3 dargestellt werden.

Ich stelle jedoch fest, dass alle Prozesse, mit denen ich untersucht lsof -p <pid>habe, einen zusätzlichen Dateideskriptor 255mit Leseberechtigung haben.

Aus dieser Antwort habe ich erfahren, dass diese Funktion spezifisch für die Bash-Shell ist. Sowohl die Antwort als auch die Quelle, auf die verwiesen wird, haben jedoch nicht wirklich erklärt, wofür dieser Dateideskriptor gedacht ist.

Meine Frage:

  1. Wofür ist der 255-Dateideskriptor?
  2. Kann ich es in meinem Bash-Skript verwenden oder handelt es sich nur um einen internen Arbeitsmechanismus, der nicht manuell verwendet / manipuliert werden soll?
Tran Triet
quelle
Meiner Meinung nach wurden Ihre Fragen auf der verlinkten Seite beantwortet.
Cyrus
Ich werde die Antwort erneut prüfen, um festzustellen, ob sie meine Frage beantwortet. Ich habe erst jetzt bemerkt, dass du derjenige bist, der diese Antwort gegeben hat;)
Tran Triet
2
@ Cyrus zu sagen, dass "es ein kleiner Trick ist", ohne zu erklären, was dieser "kleine Trick" ist, ist keine richtige Antwort.
Mosvy
Der erste Kommentar zu der verknüpften Antwort scheint eine bessere Diskussion zu haben ... Die letzte Antwort ist wahrscheinlich das, wonach Sie suchen ...
RubberStamp
Siehe auch
Isaac

Antworten:

12

Zum letzten Teil Ihrer Frage:

kann ich es benutzen?

Von man bash:

Umleitungen mit Dateideskriptoren größer als 9 sollten mit Vorsicht verwendet werden, da sie möglicherweise mit Dateideskriptoren in Konflikt stehen, die die Shell intern verwendet.

Wenn Sie also die Erstellung eines neuen fd mit dieser Nummer verwenden möchten, lautet die Antwort nein.

Wenn du meinst, benutze als: "schreibe auf diesen fd":

$ echo hello >/dev/fd/255"

Oder daraus zu lesen:

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

Die Antwort ist ja.
Aber wahrscheinlich sollte es besser (unabhängig von der Shell) sein, um /dev/ttyauf die zuzugreifen tty.

Wofür ist der Dateideskriptor 255?

Als alternative Verbindung zum tty für den Fall, dass fd 1 ( /dev/stdout) und fd 0 ( /dev/stdin) blockiert werden.

Mehr Details .

Andere Shells verwenden möglicherweise eine andere Nummer (z. B. 10 in 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% 

Aus der Mailingliste :

Fd 255 wird intern als Verbindung zum tty verwendet, damit die Verwendung von exec zum Verschieben von fds nicht beeinträchtigt wird. Bash weist aus dem gleichen Grund auch hohe fds zu, wenn eine Prozesssubstitution "<(foo)" behandelt wird.
Andreas Schwab

Isaac
quelle
fd 255 wird nicht verwendet, um "eine Kopie von fd 1 und fd 0 aufzubewahren" - Sie können dies leicht mit überprüfen dd bs=1 | bash -i -c 'sleep .1; ls -l /proc/$$/fd' 2>/tmp/err | tee /tmp/out. Außerdem geht es bei diesem Kommentar aus der Mailingliste darum, wann er ausgeführt bashwird bash scriptfile( 255in diesem Fall ist das offene Handle für scriptfile- und in diesem Fall ls -l /proc/pid/fdwird er sehr überzeugend gedruckt 255 -> scriptfile;-)), und nicht darum, wann er interaktiv ausgeführt wird.
Mosvy
Es tut mir leid, dass die Quellcode-Schnipsel und die Analyse aus meiner Antwort Sie nicht überzeugt haben, aber um ganz klar zu sein: a) Es ist keine "alternative" Verbindung zum tty, sondern die Hauptverbindung zum tty, die für alle verwendet wird tty bezogene Zwecke b) sie von fd 2 (stderr) oder geöffnet direkt aus kopiert wird /dev/tty, nicht von fd 0 oder fd 1 c) wenn fds 0, 1 oder 2 get "blockiert", bash wird nicht verwenden , dass 255 fd als Alternative für Lesen von Eingaben vom Benutzer oder zum Schreiben von Befehlsausgaben, Eingabeaufforderungen, Fehlermeldungen usw.
Mosvy
8

Dieser 255Dateideskriptor ist ein offenes Handle für das steuernde tty und wird nur verwendet, wenn er bashim interaktiven Modus ausgeführt wird.

Sie können das stderrin der Haupt-Shell umleiten , während die Jobsteuerung weiterhin funktioniert (dh Prozesse mit ^ C beenden, mit ^ Z unterbrechen usw.).

Beispiel:

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

Wenn Sie dies in einer Shell wie versuchen ksh93, die einfach den Dateideskriptor 2 als Referenz für das steuernde Terminal verwendet, wird der sleepProzess immun gegen ^ C und ^ Z und muss aus einem anderen Fenster / einer anderen Sitzung beendet werden. Dies liegt daran, dass die Shell die Prozessgruppe von nicht sleepals Vordergrundgruppe im Terminal mit festlegen kann tcsetgrp(), da der Dateideskriptor 2 nicht mehr auf das Terminal zeigt.

Dies ist nicht bashspezifisch, es wird auch in dashund verwendet zsh, nur dass der Deskriptor nicht so hoch verschoben wird (normalerweise sind es 10).

zsh wird dieses fd auch verwenden, um Eingabeaufforderungen und Benutzereingaben wiederzugeben, sodass einfach Folgendes funktioniert:

$ exec 2>/tmp/err
$ 

Es hat nichts mit den bashDateihandles zu tun, die beim Lesen von Skripten und beim Einrichten von Pipes verwendet werden (die mit derselben Funktion ebenfalls aus dem Weg geräumt werden - move_to_high_fd()), wie dies in anderen Antworten und Kommentaren vorgeschlagen wurde.

bashverwendet eine so große Anzahl, um zu ermöglichen, dass fds größer sind als 9bei In-Shell-Umleitungen (z. B. exec 87<filename); Das wird in anderen Shells nicht unterstützt.

Sie können dieses Dateihandle selbst verwenden, aber es macht wenig Sinn, dies zu tun, da Sie in jedem Befehl mit ein Handle für dasselbe steuernde Terminal erhalten können ... < /dev/tty.

Quellcode-Analyse von Bash :

In bashwird der Dateideskriptor des steuernden Terminals in der shell_ttyVariablen gespeichert . Wenn die Shell interaktiv ist, wird diese Variable (beim Start oder nach einer fehlgeschlagenen Ausführung) jobs.c:initialize_job_control()durch Dup'ing von stderr(wenn stderran ein Terminal angeschlossen) oder durch direktes Öffnen /dev/ttyinitialisiert und dann erneut auf einen höheren fd dup'ed mit 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);
      ...
    }

Wenn shell_ttynicht bereits die steuernde tty ist, dann wird es so gemacht:

          /* 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 ist dann daran gewöhnt

  1. Holen und setzen Sie die Vordergrundprozessgruppe mit tc[sg]etpgrpin jobs.c:maybe_give_terminal_to(), jobs.c:set_job_control()undjobs.c:give_terminal_to()

  2. termios(3)Holen Sie sich und setzen Sie die Parameter in jobs.c:get_tty_state()undjobs.c:set_tty_state()

  3. Holen Sie sich die Größe des Terminalfensters mit ioctl(TIOCGWINSZ)in lib/sh/winsize.c:get_new_window_size().

move_to_high_fd()wird im Allgemeinen mit allen temporären Dateideskriptoren verwendet, die von bash(Skriptdateien, Pipes usw.) verwendet werden, daher die Verwirrung in den meisten Kommentaren, die bei Google-Suchanfragen häufig vorkommen.

Die intern von basheinschließlich verwendeten Dateideskriptoren shell_ttysind alle auf close-on-exec eingestellt, damit sie nicht an Befehle weitergegeben werden.

Mosvy
quelle