Ошибка перенаправления файлов Bash?
Из того, что я понимаю о программе IO, есть stdin
, stdout
, а также stderr
потоки данных (и код возврата). stdout
а также stderr
два потока вывода данных. Поэтому, если я использую перенаправление bash, чтобы закрыть один из выходных потоков, я могу сузить, в какой поток отправляется этот текст. Правильно?
Я использую Ubuntu 18.04.1 LTS и сталкиваюсь с этой странной проблемой с перенаправлением bash.
Позвольте мне объяснить пример. Вот моя команда:
# apt-cache show php5
N: Can't select versions from package 'php5' as it is purely virtual
N: No packages found
# |
php5
пакет не существует в Ubuntu 18.04, поэтому apt-cache
отображает ошибку. Я бы предположил, что этот текст отправлен stderr
поток, поэтому я попытался закрыть этот поток:
# apt-cache show php5 2>&-
# |
Похоже, это подтверждает, что текст был отправлен через stderr
, Пока все хорошо! Но теперь, как проверка работоспособности, я попытался закрыть stdout
(Я должен увидеть текст ошибки сейчас):
# apt-cache show php5 1>&-
# |
Какие!? Я перенаправил stdout
на этот раз, но stderr
тоже не появляется?
Согласно Интернету у меня правильные дескрипторы файлов: https://www.gnu.org/software/bash/manual/html_node/Redirections.html
Я не могу понять, что здесь может происходить.
Снимок экрана:
1 ответ
TL;DR: это не bash
, его apt-cache
это возиться с файловыми дескрипторами.
apt-cache
делает что-то очень интересное - он не пишет строки, начинающиеся с N:
символы, предназначенные для stdout.
Учти это:
$ apt-cache show nonexistent
N: Unable to locate package nonexistent
E: No packages found
Мы видим две строки, одна из которых начинается с N:
один начинается с E:
, N:
линии идут на стандартный вывод. В вашем примере у вас есть два N:
линий.
# apt-cache show php5
N: Can't select versions from package 'php5' as it is purely virtual
N: No packages found
Если вы отслеживаете системные вызовы через strace -e write -f bash -c 'apt-cache show randomtext >&-'
вы увидите, что письмо E:
линии случаются, но N
линий там нет:
[pid 12450] write(2, "E", 1E) = 1
[pid 12450] write(2, ": ", 2: ) = 2
[pid 12450] write(2, "No packages found", 17No packages found) = 17
[pid 12450] write(2, "\n", 1
) = 1
[pid 12450] +++ exited with 100 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=12450, si_uid=1000, si_status=100, si_utime=5, si_stime=2} ---
+++ exited with 100 +++
Так apt-cache
достаточно умен, чтобы проверить перенаправленный стандартный вывод. Но что насчет stderr
? Видимо пишет еще там: если вы делаете strace -e write,openat,dup2 -f bash -c 'apt-cache show randomtext 2>&-
вы увидите, что apt-cache
открывает /dev/null
чтобы еще было что-то для stderr
:
[pid 12543] openat(AT_FDCWD, "/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 2
....
[pid 12543] write(2, "N", 1) = 1
[pid 12543] write(2, ": ", 2) = 2
[pid 12543] write(2, "Unable to locate package randomt"..., 35) = 35
[pid 12543] write(2, "\n", 1) = 1
[pid 12543] write(2, "E", 1) = 1
[pid 12543] write(2, ": ", 2) = 2
[pid 12543] write(2, "No packages found", 17) = 17
[pid 12543] write(2, "\n", 1) = 1
[pid 12543] +++ exited with 100 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=12543, si_uid=1000, si_status=100, si_utime=5, si_stime=3} ---
+++ exited with 100 +++
Если вы выполняете то же самое с другими программами в bash, это работает как положено:
# stdout closed, stderr not
$ ls -l /proc/self/fd >&-
ls: write error: Bad file descriptor
# stdout open , stderr closed, and it's number is assigned to whatever command is trying to open - in this case /proc/self/fd directory
$ ls -l /proc/self/fd 2>&-
total 0
lrwx------ 1 xie xie 64 Oct 6 11:32 0 -> /dev/pts/1
lrwx------ 1 xie xie 64 Oct 6 11:32 1 -> /dev/pts/1
lr-x------ 1 xie xie 64 Oct 6 11:32 2 -> /proc/12723/fd