Я пытался перенаправить обоих stdout
и stderr
в файл сегодня, и я столкнулся с этим:
<command> > file.txt 2>&1
Это, по-видимому, перенаправляет stderr
кому: stdout
во-первых, и затем получающееся stdout
перенаправляется к file.txt
.
Однако, почему порядок не <command> 2>&1 > file.txt
? Можно было бы естественно считать это как (принятие выполнения слева направо) команда, выполняемая сначала, stderr
быть перенаправленным к stdout
и затем, получающееся stdout
быть записанным в file.txt
. Но вышеупомянутое только перенаправляет stderr
на экран.
Как оболочка интерпретирует обоих команды?
Когда Вы работаете <command> 2>&1 > file.txt
, stderr перенаправляется 2>&1
туда, где stdout в настоящее время идет, Ваш терминал. После этого stdout перенаправляется в файл >
, но stderr не перенаправляется с ним, поэтому остается как терминальный вывод.
С <command> > file.txt 2>&1
stdout сначала перенаправляется в файл >
, затем 2>&1
перенаправления stderr туда, где stdout идет, который является файлом.
может казаться интуитивным счетчиком для начала, но когда Вы думаете о перенаправлениях таким образом и помните, что они обрабатываются слева направо, это имеет намного больше смысла.
Я думаю, что это помогает думать, что оболочка настроит перенаправление слева сначала, и завершенный это перед установкой следующего перенаправления.
Командная строка Linux William Shotts говорит
Первый, мы перенаправляем стандартный вывод в файл, и затем мы перенаправляем дескриптор файла 2 (стандартная погрешность) к дескриптору файла один (стандартный вывод)
, это имеет смысл, но затем
Уведомление, что порядок перенаправлений является значительным. Перенаправление стандартной погрешности должно всегда происходить после перенаправления стандартного вывода, или это не работает
, но на самом деле, мы можем перенаправить stdout к stderr после перенаправления stderr в файл с тем же эффектом
$ uname -r 2>/dev/null 1>&2
$
Так, в command > file 2>&1
, оболочка отправляет stdout в файл, затем отправляет stderr в stdout (который отправляется в файл). Принимая во внимание, что, в command 2>&1 > file
оболочка сначала перенаправляет stderr к stdout (т.е. отображает его в терминале, куда stdout обычно идет), и затем впоследствии, stdout перенаправлений в файл. TLCL вводит в заблуждение в высказывании, что мы должны перенаправить stdout сначала: так как мы можем перенаправить stderr в файл сначала и затем отправить stdout в него. То, что мы не можем сделать, является перенаправлением stdout к stderr или наоборот прежде перенаправление в файл. Другой пример
$ strace uname -r 1>&2 2> /dev/null
4.8.0-30-generic
Мы могли бы думать, что это избавится от stdout к тому же месту как stderr, но это не делает, это перенаправляет stdout к stderr (экран) сначала и затем только перенаправляет stderr, как тогда, когда мы попробовали его наоборот...
я надеюсь, что это приносит определенный свет...
Порядок слева направо. Руководство Bash уже касалось то, что Вы спрашиваете. Кавычка от REDIRECTION
раздел руководства:
Redirections are processed in the
order they appear, from left to right.
и несколько строк позже:
Note that the order of redirections is signifi‐
cant. For example, the command
ls > dirlist 2>&1
directs both standard output and standard error
to the file dirlist, while the command
ls 2>&1 > dirlist
directs only the standard output to file
dirlist, because the standard error was dupli‐
cated from the standard output before the stan‐
dard output was redirected to dirlist.
важно отметить, что перенаправление разрешено сначала перед любыми выполненными командами! См. https://askubuntu.com/a/728386/295286
Это всегда слева направо... кроме тех случаев, когда
Точно так же, как в Математике мы делаем слева направо кроме умножения, и подразделение сделано перед дополнением и вычитанием, операции исключения в круглой скобке (+-) были бы сделаны перед умножением и разделением.
Согласно руководству новичков Bash здесь ( Руководство Новичков Bash ) существует 8 заказов иерархии того, что на первом месте (перед слева направо):
, Таким образом, это всегда слева направо... кроме тех случаев, когда...
Это могло бы иметь смысл при трассировке его.
В начале stderr и stdout переходят к тому же самому (обычно терминал, который я называю здесь pts
):
fd/0 -> pts
fd/1 -> pts
fd/2 -> pts
Я обращаюсь к stdin, stdout и stderr их числами дескриптора файла здесь: они - дескрипторы файлов 0, 1 и 2 соответственно.
Теперь, в первом наборе перенаправлений, мы имеем > file.txt
и 2>&1
.
Так:
> file.txt
: fd/1
теперь переходит в file.txt
. С >
, 1
подразумеваемый дескриптор файла, когда ничто не указано, таким образом, это 1>file.txt
:
fd/0 -> pts
fd/1 -> file.txt
fd/2 -> pts
2>&1
: fd/2
теперь переходит в везде, где fd/1
в настоящее время идет:
fd/0 -> pts
fd/1 -> file.txt
fd/2 -> file.txt
С другой стороны, с 2>&1 > file.txt
, порядок инвертируется:
2>&1
: fd/2
теперь переходит в везде, где fd/1
в настоящее время идет, что означает, что ничто не изменяется:
fd/0 -> pts
fd/1 -> pts
fd/2 -> pts
> file.txt
: fd/1
теперь переходит в file.txt
:
fd/0 -> pts
fd/1 -> file.txt
fd/2 -> pts
Важный момент - то, что перенаправление не означает, что перенаправленный дескриптор файла будет следовать за всеми будущими изменениями в дескрипторе конечного файла; это только возьмет текущее состояние.
Вы уже получили несколько очень хороших ответов. Позвольте мне подчеркнуть хотя это, там два различных понятия, включенные здесь, понимание которого помогает чрезвычайно:
Ваш дескриптор файла является просто номером 0... n, который является индексом в таблице дескрипторов файлов в Вашем процессе. Условно, STDIN=0, STDOUT=1, STDERR=2 (отмечают что условия STDIN
и т.д. вот просто символы/макросы, используемые условно на некоторых языках программирования и страницах справочника, нет фактического "объекта" под названием STDIN; в целях этого обсуждения STDIN 0, и т.д.).
Та таблица дескрипторов файлов сам по себе не содержит информации вообще о том, каков фактический файл. Вместо этого это содержит указатель на другую таблицу файлов; последний содержит информацию о фактическом физическом файле (или блочное устройство или канал, или независимо от того, что Linux может обратиться с помощью механизма файла) и больше информации (т.е. является ли это для чтения или записи).
Таким образом, когда Вы используете >
или <
в Вашей оболочке Вы просто заменяете указатель соответствующего дескриптора файла для указания на что-то еще. Синтаксис 2>&1
просто дескриптор точек 2, к везде, где 1 точка. > file.txt
просто открывается file.txt
для записи и позволяет STDOUT (файл decsriptor 1) точка к этому.
Существуют другие положительные герои, например. 2>(xxx)
(т.е.: создайте новое выполнение процесса xxx
, создайте канал, соедините дескриптор файла 0 из нового процесса к концу чтения канала и соедините дескриптор файла 2 из исходного процесса к концу записи канала).
Это - также основание для "волшебства дескриптора файла" в другом программном обеспечении, чем Ваша оболочка. Например, Вы могли в Вашем сценарии Perl, dup
licate дескриптор файла STDOUT в другого (временный), затем вновь откройте STDOUT для недавно созданного временного файла. С этого момента, весь вывод STDOUT из Вашего собственного сценария Perl и всех system()
вызовы того сценария закончатся в том временном файле. При выполнении Вы можете пере -dup
Ваш STDOUT к временному дескриптору, Вы сохранили его к, и престо, все как прежде. Можно даже записать в тот временный дескриптор между тем, поэтому в то время как фактический вывод STDOUT переходит к временному файлу, можно все еще на самом деле произвести материал к реальному STDOUT (обычно, пользователь).
Применять справочную информацию, данную выше Вашему вопросу:
В каком порядке оболочка выполняет потоковое перенаправление и команды?
Слева направо.
<command> > file.txt 2>&1
fork
от нового процесса.file.txt
и сохраните его указатель в дескрипторе файла 1 (STDOUT).file.txt
конечно).exec
<command>
Это, по-видимому, перенаправляет stderr к stdout сначала, и затем получающийся stdout перенаправляется к file.txt.
Это имело бы смысл, если бы была только одна таблица, но, как объяснено выше существует два. Дескрипторы файлов не указывают друг на друга рекурсивно, не имеет никакого смысла думать "перенаправление STDERR к STDOUT". Корректная мысль является "точкой STDERR к тому, везде, где STDOUT указывает". При изменении STDOUT позже STDERR остается, где это, он волшебно не соглашается с дальнейшими изменениями в STDOUT.