Я пытался перенаправить оба stdout
и stderr
в файл сегодня, и я столкнулся с этим:
<command> > file.txt 2>&1
Это, по-видимому, перенаправляет stderr
в stdout
первую очередь, а затем в результате stdout
перенаправляется в file.txt
.
Однако почему не заказ <command> 2>&1 > file.txt
? Естественно, можно было бы прочитать это как (при условии выполнения слева направо) сначала выполняемую команду stderr
, stdout
а затем перенаправлять в полученную stdout
запись file.txt
. Но вышеперечисленное только перенаправляет stderr
на экран.
Как оболочка интерпретирует обе команды?
command-line
bash
redirect
Поезд Heartnet
источник
источник
bash
руководстве . Кстати, перенаправления не являются командами.execv
-семейство системного вызова вызываются на самом деле передать подпроцесс прочь к команде быть запущен, оболочка затем выходит из цикла (больше не имеет код , выполняемый в этом процессе ) и не имеет возможности контролировать, что происходит с этого момента; Таким образом, все перенаправления должны быть выполнены до начала выполнения, в то время как оболочка имеет свою собственную копию, запущенную в процессе, в котором онаfork()
выполняла вашу команду.Ответы:
Когда ты бежишь
<command> 2>&1 > file.txt
stderr, вы перенаправляетесь2>&1
туда, куда в данный момент идет stdout, на ваш терминал. После этого stdout перенаправляется в файл>
, но stderr не перенаправляется с ним, поэтому остается выводом терминала.С
<command> > file.txt 2>&1
STDOUT сначала перенаправляется в файл с помощью>
, а затем2>&1
перенаправляет стандартный вывод, где стандартный вывод будет, что файл.На первый взгляд может показаться нелогичным, но когда вы думаете о перенаправлениях таким образом и помните, что они обрабатываются слева направо, это имеет гораздо больше смысла.
источник
Это может иметь смысл, если вы проследите это.
В начале stderr и stdout идут в одно и то же (обычно это терминал, который я здесь называю
pts
):Я имею в виду stdin, stdout и stderr по номерам их дескрипторов файлов : они являются дескрипторами файлов 0, 1 и 2 соответственно.
Теперь в первом наборе перенаправлений у нас есть
> file.txt
и2>&1
.Так:
> file.txt
: Вfd/1
настоящее время идетfile.txt
. С>
,1
подразумевается дескриптор файла, когда ничего не указано, так что это1>file.txt
:2>&1
:fd/2
теперь идет туда, кудаfd/1
сейчас идет:С другой стороны, с
2>&1 > file.txt
изменением порядка:2>&1
:fd/2
теперь идет туда, куда идет вfd/1
данный момент, что означает, что ничего не меняется:> file.txt
: Вfd/1
настоящее время идетfile.txt
:Важным моментом является то, что перенаправление не означает, что дескриптор перенаправленного файла будет следовать за всеми будущими изменениями дескриптора целевого файла; это только примет текущее состояние.
источник
2.
второй части;fd/1 -> file.txt
и нетfd/2 -> file.txt
.Я думаю, что это помогает думать, что оболочка сначала установит перенаправление слева, и завершит его перед установкой следующего перенаправления.
Командная строка Linux Уильяма Шоттса говорит
это имеет смысл, но потом
но на самом деле мы можем перенаправить stdout в stderr после перенаправления stderr в файл с тем же эффектом
Таким образом
command > file 2>&1
, оболочка отправляет стандартный вывод в файл, а затем отправляет стандартный вывод в стандартный вывод (который отправляется в файл). Принимая во внимание, что вcommand 2>&1 > file
оболочке сначала перенаправляет stderr на стандартный вывод (т.е. отображает его в терминале, куда обычно идет стандартный вывод), а затем после этого перенаправляет стандартный вывод в файл. TLCL вводит в заблуждение, говоря, что сначала мы должны перенаправить stdout: так как сначала мы можем перенаправить stderr в файл, а затем отправить ему stdout. Чего мы не можем сделать, так это перенаправить стандартный вывод в стандартный поток ошибок или наоборот перед перенаправлением в файл. Другой примерМы могли бы подумать, что это избавит от stdout в том же месте, что и stderr, но это не так, сначала он перенаправляет stdout на stderr (экран), а затем перенаправляет только stderr, как когда мы пробовали его наоборот ...
Я надеюсь, что это приносит немного света ...
источник
Вы уже получили несколько очень хороших ответов. Позвольте мне подчеркнуть, что здесь задействованы две разные концепции, понимание которых очень помогает:
Справочная информация: дескриптор файла против таблицы файлов
Ваш дескриптор файла это просто число 0 ... n, которое является индексом в таблице дескрипторов файлов в вашем процессе. По соглашению, STDIN = 0, STDOUT = 1, STDERR = 2 (обратите внимание, что термины
STDIN
и т. Д. Здесь являются просто символами / макросами, используемыми соглашением в некоторых языках программирования и на страницах руководства, не существует фактического «объекта», называемого STDIN; целью этого обсуждения является STDIN 0 и т. д.).Эта таблица дескрипторов файлов сама по себе не содержит никакой информации о том, что представляет собой файл. Вместо этого он содержит указатель на другую таблицу файлов; последний содержит информацию о реальном физическом файле (или блочном устройстве, или канале, или обо всем, что Linux может адресовать через файловый механизм) и дополнительную информацию (т. е. для чтения или записи).
Поэтому, когда вы используете
>
или<
в своей оболочке, вы просто заменяете указатель соответствующего дескриптора файла, чтобы указать на что-то еще. Синтаксис2>&1
просто указывает дескриптор 2 на 1 балл.> file.txt
просто открываетсяfile.txt
для записи и позволяет STDOUT (файл-дескриптор 1) указать на это.Есть и другие полезности, например
2>(xxx)
(например: создать новый процесс, работающийxxx
, создайте канал, подключите дескриптор файла 0 нового процесса к концу чтения канала и подключите дескриптор файла 2 исходного процесса к концу записи труба).Это также основа для "волшебства дескриптора файла" в другом программном обеспечении, кроме вашей оболочки. Например, вы можете в своем скрипте Perl
dup
лицензировать дескриптор файла STDOUT в другой (временный), а затем снова открыть STDOUT для вновь созданного временного файла. С этого момента все выходные данные STDOUT из вашего собственного сценария Perl и всеsystem()
вызовы этого сценария попадут в этот временный файл. Когда вы закончите, вы можете вернутьdup
свой STDOUT во временный дескриптор, в котором вы его сохранили, и до того, как все будет как раньше. Тем временем вы даже можете записать в этот временный дескриптор, так что, хотя ваш фактический вывод STDOUT переходит во временный файл, вы все равно можете фактически выводить материал в реальный STDOUT (обычно пользователь).Ответ
Чтобы применить приведенную выше справочную информацию к вашему вопросу:
Слева направо.
fork
от нового процесса.file.txt
и сохраните его указатель в дескрипторе файла 1 (STDOUT).file.txt
разумеется, уже открыт ).exec
<command>
Это имело бы смысл, если бы была только одна таблица, но, как объяснено выше, их две. Файловые дескрипторы не указывают рекурсивно друг на друга, нет смысла думать «перенаправить STDERR в STDOUT». Правильная мысль: «Направь STDERR туда, куда указывает STDOUT». Если вы измените STDOUT позже, STDERR останется там, где он есть, он волшебным образом не согласуется с дальнейшими изменениями в STDOUT.
источник
Порядок слева направо. Руководство Bash уже охватывает то, что вы спрашиваете. Цитата из
REDIRECTION
раздела руководства:и несколько строк спустя:
Важно отметить, что перенаправление разрешается в первую очередь перед выполнением любых команд! См. Https://askubuntu.com/a/728386/295286.
источник
Это всегда слева направо ... кроме случаев, когда
Как и в математике, мы делаем слева направо, за исключением того, что умножение и деление выполняются до сложения и вычитания, за исключением операций внутри круглых скобок (+ -), которые выполняются до умножения и деления.
В соответствии с руководством для начинающих Bash здесь ( Руководство для начинающих Bash ) существует 8 уровней иерархии того, что идет первым (перед слева направо):
Так что всегда слева направо ... кроме случаев, когда ...
источник