Как работают файловые дескрипторы?



может кто-нибудь сказать мне, почему это не работает? Я играю с файловыми дескрипторами, но чувствую себя немного потерянным.



#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4


первые три строки работают нормально,но последние две ошибки. Зачем?

769   3  

3 ответов:

файловые дескрипторы 0, 1 и 2 предназначены для stdin, stdout и stderr соответственно.

файловые дескрипторы, 3, 4, .. 9 для дополнительных файлов. Для того, чтобы использовать их, вы должны открыть их в первую очередь. Например:

exec 3<> /tmp/foo  #open fd 3.
echo "test" >&3
exec 3>&- #close fd 3.

для получения дополнительной информации взгляните на Advanced Bash-Scripting Guide: Глава 20. Перенаправление Ввода-Вывода.

это старый вопрос, но одна вещь нуждается в разъяснении.

пока ответов Карл Норум и dogbane верны, предположение измените свой скрипт, чтобы он работал.

я хотел бы отметить, что вам не нужно менять скрипт:

#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4

это работает, если вы вызываете его по-разному:

./fdtest 3>&1 4>&1

что означает перенаправление дескрипторов файлов 3 и 4 в 1 (который является стандартным выходом).

дело в том, что скрипт отлично работает в желании писать в дескрипторы, отличные от 1 и 2 (stdout и stderr) если эти дескрипторы предоставлены родительским процессом.

ваш пример на самом деле довольно интересен, потому что этот скрипт может писать в 4 разных файла:

./fdtest >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt

теперь у вас есть выход в 4 отдельных файлах:

$ for f in file*; do echo $f:; cat $f; done
file1.txt:
This
file2.txt:
is
file3.txt:
a
file4.txt:
test.

что интереснее о том, что ваша программа не должна иметь разрешения на запись для этих файлов, потому что она на самом деле не открывает их.

например, когда я запускаю sudo -s чтобы изменить пользователя на root, создайте каталог как root и попробуйте выполнить следующую команду как мой обычный пользователь (rsp в моем случае) следующим образом:

# su rsp -c '../fdtest >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt'

я получаю сообщение об ошибке:

bash: file1.txt: Permission denied

но если я делаю перенаправление за пределами su:

# su rsp -c '../fdtest' >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt

(обратите внимание на разницу в одинарные кавычки) он работает а я:

# ls -alp
total 56
drwxr-xr-x 2 root root 4096 Jun 23 15:05 ./
drwxrwxr-x 3 rsp  rsp  4096 Jun 23 15:01 ../
-rw-r--r-- 1 root root    5 Jun 23 15:05 file1.txt
-rw-r--r-- 1 root root   39 Jun 23 15:05 file2.txt
-rw-r--r-- 1 root root    2 Jun 23 15:05 file3.txt
-rw-r--r-- 1 root root    6 Jun 23 15:05 file4.txt

которые являются 4 файлов, принадлежащих root в каталоге, принадлежащем root -хотя у скрипта не было разрешений для создания этих файлов.

другой пример будет использовать chroot jail или контейнер и запускать программу внутри, где она не будет иметь доступа к этим файлам, даже если она была запущена как root и все еще перенаправляет их дескрипторы внешне там, где вам нужно, без фактического предоставления доступа ко всей файловой системе или чему-либо еще к этому сценарию.

дело в том, что вы открыли очень интересный и полезный механизм. Вам не нужно открывать все файлы внутри вашего скрипта, как было предложено в другой ответ. Иногда полезно перенаправить их во время вызова скрипта.

в итоге это:

echo "This"

is фактически эквивалентно:

echo "This" >&1

и запуск программы, как:

./program >file.txt

- это то же, что:

./program 1>file.txt

число 1-это просто номер по умолчанию, и это stdout.

но даже эта программа:

#!/bin/bash
echo "This"

может привести к ошибке" плохой дескриптор". Как? При запуске как:

./fdtest2 >&-

вывод будет:

./fdtest2: line 2: echo: write error: Bad file descriptor

добавлять >&- (что то же самое, что 1>&-) означает закрытие стандартный выход. Добавление 2>&- означало бы закрытие stderr.

вы даже можете сделать более сложная штука. Ваш оригинальный сценарий:

#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4

при запуске с так:

./fdtest

принты:

This
is
./fdtest: line 4: 3: Bad file descriptor
./fdtest: line 5: 4: Bad file descriptor

но вы можете заставить дескрипторы 3 и 4 работать, но номер 1 не работает, запустив:

./fdtest 3>&1 4>&1 1>&-

он выводит:

./fdtest: line 2: echo: write error: Bad file descriptor
is
a
test.

если вы хотите, чтобы дескрипторы 1 и 2 потерпели неудачу, запустите его как это:

./fdtest 3>&1 4>&1 1>&- 2>&-

вы получаете:

a
test.

почему? Ничего не подвело? это ничего но без stderr (файловый дескриптор номер 2)вы не видели сообщения об ошибках!

я думаю, что очень полезно поэкспериментировать таким образом, чтобы получить представление о том, как работают дескрипторы и их перенаправление.

ваш сценарий действительно очень интересный пример-и я утверждаю, что он не сломан вообще, вы были просто используя его неправильно! :)

это не удается, потому что эти дескрипторы файлов не указывают ни на что! Обычные файловые дескрипторы по умолчанию являются стандартным вводом 0 стандартный выход 1, и стандартный поток ошибок 2. Поскольку ваш скрипт не открывает никаких других файлов, нет других допустимых файловых дескрипторов. Вы можете открыть файл в bash с помощью exec. Вот модификация вашего примера:

#!/bin/bash
exec 3> out1     # open file 'out1' for writing, assign to fd 3
exec 4> out2     # open file 'out2' for writing, assign to fd 4

echo "This"      # output to fd 1 (stdout)
echo "is" >&2    # output to fd 2 (stderr)
echo "a" >&3     # output to fd 3
echo "test." >&4 # output to fd 4

а теперь мы запустим его:

$ ls
script
$ ./script 
This
is
$ ls
out1    out2    script
$ cat out*
a
test.
$

как вы можете видеть, дополнительные вывод был отправлен в запрошенные файлы.

Comments

    Ничего не найдено.