Файловые дескрипторы Bash против файловых дескрипторов Linux

#linux #bash

#linux #баш

Вопрос:

Я просто пытаюсь примирить эти две, казалось бы, похожие концепции.

В Bash разрешено делать произвольные перенаправления и, что важно, использовать выбранный номер файлового дескриптора. Однако в Linux значение, возвращаемое open вызовом (AFAIK), не может быть выбрано вызывающим процессом.

Таким образом, совпадают ли номера fd Bash с номерами fd, возвращаемыми системными вызовами? Если нет, то в чем разница?

Ответ №1:

Вот небольшой эксперимент, который может пролить некоторый свет на то, что происходит, когда вы открываете файловый дескриптор в bash с выбранным вами номером:

 > cat test.txt
foobar!

> cat test.sh
#!/bin/bash
exec 17<test.txt
read -u 17 line
echo "$line"
exec 17>amp;-

> strace ./test.sh
//// A bunch of stuff omitted so we can skip to the interesting part...
open("test.txt", O_RDONLY)              = 3
fcntl(17, F_GETFD)                      = -1 EBADF (Bad file descriptor)
dup2(3, 17)                             = 17
close(3)                                = 0
fcntl(17, F_GETFD)                      = 0
ioctl(17, TCGETS, 0x7ffc56f093f0)       = -1 ENOTTY (Inappropriate ioctl for device)
lseek(17, 0, SEEK_CUR)                  = 0
read(17, "foobar!n", 128)              = 8
write(1, "foobar!n", 8foobar!)         = 8
fcntl(17, F_GETFD)                      = 0
fcntl(17, F_DUPFD, 10)                  = 10
fcntl(17, F_GETFD)                      = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
close(17)                               = 0
 

Часть, которая отвечает на ваш вопрос, — это то, где он вызывает open() test.txt , который возвращает значение 3. Это то, что вы, скорее всего, получили бы в программе на языке Си, если бы сделали то же самое, потому что файловые дескрипторы 0, 1 и 2 (т. Е. , stdin , stdout , и stderr ) — это все, что у вас открыто изначально. Число 3 — это просто следующий доступный файловый дескриптор.

И мы видим это и в strace выходных данных скрипта bash. Что bash делает по-другому, так это то, что затем он вызывает fcntl(17, F_GETFD) , чтобы проверить, открыт ли уже файловый дескриптор 17 (потому что он хочет использовать этот fd для test.txt ). Затем, когда fcntl возвращаются EBADF результаты, указывающие на то, что такой fd не открыт, bash знает, что он может свободно его использовать. Итак, затем он призывает dup2(3, 17) сделать fd 17 копией fd 3. Наконец, он вызывает close() fd 3, чтобы снова освободить его, оставляя fd 17 (и только fd 17) в качестве открытого файлового дескриптора для test.txt .

Итак, ответ на ваш вопрос заключается в том, что файловые дескрипторы bash не являются особыми существами, отличными от «обычных» файловых дескрипторов, которые используют все остальные. На самом деле это одно и то же. Вы могли бы легко использовать тот же трюк в своей программе на языке Си для открытия файлов с номерами файловых дескрипторов по вашему выбору.

Кроме того, стоит отметить, что bash на самом деле не может выбирать свой собственный файловый дескриптор при вызове open() . Он должен довольствоваться тем, что open() возвращается, как и все остальные. Все, что на самом деле происходит в вашем bash-скрипте, — это немного дыма и зеркал (via dup2() ), чтобы создать впечатление, что вы можете выбрать свой собственный файловый дескриптор.