В каком порядке оболочка выполняет потоковое перенаправление и команды?

Я пытался перенаправить обоих stdout и stderr в файл сегодня, и я столкнулся с этим:

<command> > file.txt 2>&1

Это, по-видимому, перенаправляет stderr кому: stdout во-первых, и затем получающееся stdout перенаправляется к file.txt.

Однако, почему порядок не <command> 2>&1 > file.txt? Можно было бы естественно считать это как (принятие выполнения слева направо) команда, выполняемая сначала, stderr быть перенаправленным к stdout и затем, получающееся stdout быть записанным в file.txt. Но вышеупомянутое только перенаправляет stderr на экран.

Как оболочка интерпретирует обоих команды?

34
задан 13 December 2016 в 04:35

6 ответов

Когда Вы работаете <command> 2>&1 > file.txt, stderr перенаправляется 2>&1 туда, где stdout в настоящее время идет, Ваш терминал. После этого stdout перенаправляется в файл >, но stderr не перенаправляется с ним, поэтому остается как терминальный вывод.

С <command> > file.txt 2>&1 stdout сначала перенаправляется в файл >, затем 2>&1 перенаправления stderr туда, где stdout идет, который является файлом.

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

41
ответ дан 23 November 2019 в 00:35

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

Командная строка 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, как тогда, когда мы попробовали его наоборот...

я надеюсь, что это приносит определенный свет...

11
ответ дан 23 November 2019 в 00:35

Порядок слева направо. Руководство 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

3
ответ дан 23 November 2019 в 00:35

Это всегда слева направо... кроме тех случаев, когда

Точно так же, как в Математике мы делаем слева направо кроме умножения, и подразделение сделано перед дополнением и вычитанием, операции исключения в круглой скобке (+-) были бы сделаны перед умножением и разделением.

Согласно руководству новичков Bash здесь ( Руководство Новичков Bash ) существует 8 заказов иерархии того, что на первом месте (перед слева направо):

  1. расширение Фигурной скобки "{}"
  2. выражение параметра "~"
  3. Shell расширения Тильды и переменное выражение "$ "
  4. $ "замены Команды (команда)"
  5. Арифметическое выражение "$ ((ВЫРАЖЕНИЕ))"
  6. Замена Процесса , о чем мы говорим здесь "< (СПИСОК)" или"> (СПИСОК)"
  7. Word Splitting "'< пространство> < вкладка> < новая строка>'"
  8. Расширение Имени файла "*", "?", и т.д.

, Таким образом, это всегда слева направо... кроме тех случаев, когда...

3
ответ дан 23 November 2019 в 00:35

Это могло бы иметь смысл при трассировке его.

В начале stderr и stdout переходят к тому же самому (обычно терминал, который я называю здесь pts):

fd/0 -> pts
fd/1 -> pts
fd/2 -> pts

Я обращаюсь к stdin, stdout и stderr их числами дескриптора файла здесь: они - дескрипторы файлов 0, 1 и 2 соответственно.

Теперь, в первом наборе перенаправлений, мы имеем > file.txt и 2>&1.

Так:

  1. > file.txt: fd/1 теперь переходит в file.txt. С >, 1 подразумеваемый дескриптор файла, когда ничто не указано, таким образом, это 1>file.txt:

    fd/0 -> pts
    fd/1 -> file.txt
    fd/2 -> pts
    
  2. 2>&1: fd/2 теперь переходит в везде, где fd/1 в настоящее время идет:

    fd/0 -> pts
    fd/1 -> file.txt
    fd/2 -> file.txt
    

С другой стороны, с 2>&1 > file.txt, порядок инвертируется:

  1. 2>&1: fd/2 теперь переходит в везде, где fd/1 в настоящее время идет, что означает, что ничто не изменяется:

    fd/0 -> pts
    fd/1 -> pts
    fd/2 -> pts
    
  2. > file.txt: fd/1 теперь переходит в file.txt:

    fd/0 -> pts
    fd/1 -> file.txt
    fd/2 -> pts
    

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

20
ответ дан 23 November 2019 в 00:35

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

Фон: Дескриптор файла по сравнению с таблицей файлов

Ваш дескриптор файла является просто номером 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, duplicate дескриптор файла STDOUT в другого (временный), затем вновь откройте STDOUT для недавно созданного временного файла. С этого момента, весь вывод STDOUT из Вашего собственного сценария Perl и всех system() вызовы того сценария закончатся в том временном файле. При выполнении Вы можете пере -dup Ваш STDOUT к временному дескриптору, Вы сохранили его к, и престо, все как прежде. Можно даже записать в тот временный дескриптор между тем, поэтому в то время как фактический вывод STDOUT переходит к временному файлу, можно все еще на самом деле произвести материал к реальному STDOUT (обычно, пользователь).

Ответ

Применять справочную информацию, данную выше Вашему вопросу:

В каком порядке оболочка выполняет потоковое перенаправление и команды?

Слева направо.

<command> > file.txt 2>&1

  1. fork от нового процесса.
  2. Открытый file.txt и сохраните его указатель в дескрипторе файла 1 (STDOUT).
  3. Укажите на STDERR (дескриптор файла 2) к тому, на что указывает fd 1 прямо сейчас (который снова уже является открыт file.txt конечно).
  4. exec <command>

Это, по-видимому, перенаправляет stderr к stdout сначала, и затем получающийся stdout перенаправляется к file.txt.

Это имело бы смысл, если бы была только одна таблица, но, как объяснено выше существует два. Дескрипторы файлов не указывают друг на друга рекурсивно, не имеет никакого смысла думать "перенаправление STDERR к STDOUT". Корректная мысль является "точкой STDERR к тому, везде, где STDOUT указывает". При изменении STDOUT позже STDERR остается, где это, он волшебно не соглашается с дальнейшими изменениями в STDOUT.

10
ответ дан 23 November 2019 в 00:35

Другие вопросы по тегам:

Похожие вопросы: