Почему поведение `command 1> file.txt 2> file.txt` отличается от` command 1> file.txt 2> & 1`?

20

Если вы хотите перенаправить как stdout, так и stderr в один и тот же файл, вы можете сделать это с помощью command 1>file.txt 2>&1или command &>file.txt. Но почему поведение command 1>file.txt 2>file.txtотличается от двух приведенных выше команд?

Ниже приведена команда проверки.

$ cat redirect.sh
#!/bin/bash

{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file.txt 2>&1
{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file1.txt 2>file1.txt
{ echo -e "error" 1>&2 && echo -e "output\noutput"; } 1>file2.txt 2>file2.txt
{ echo -e "output" && echo -e "error\nerror" 1>&2; } 1>file3.txt 2>file3.txt
{ echo -e "error\nerror" 1>&2 && echo -e "output"; } 1>file4.txt 2>file4.txt

$ ./redirect.sh

$ echo "---file.txt---"; cat file.txt;\
echo "---file1.txt---"; cat file1.txt; \
echo "---file2.txt---"; cat file2.txt; \
echo "---file3.txt---"; cat file3.txt; \
echo "---file4.txt----"; cat file4.txt;
 ---file.txt---
output
output
error
---file1.txt---
error

output
---file2.txt---
output
output
---file3.txt---
error
error
---file4.txt----
output
rror

Что касается результатов, то похоже, что вторая строка эха перезаписывает первую строку эха при запуске command 1>file.txt 2>file.txt, но я не знаю, почему это произойдет. (Есть ссылка где-нибудь?)

fhiyo
источник

Ответы:

43

Вам нужно знать две вещи:

  • Дескриптор открытого файла, известный стороне приложения в режиме процесса, ссылается на внутренний объект ядра, известный как описание файла , который является экземпляром открытого файла. Может быть несколько описаний файлов на файл и несколько файловых дескрипторов, совместно использующих описание файла.
  • Текущая позиция файла является атрибутом описания файла . Таким образом, если несколько файловых дескрипторов отображаются на одно описание файла, они все совместно используют одну и ту же текущую файловую позицию, и изменение в файловой позиции, принятое с использованием одного такого файлового дескриптора, влияет на все другие такие файловые дескрипторы.

    Такие изменения введены в действие процессы , призывающих к read()/ readv(), write()/ writev(), lseek()и тому подобные системные вызовы. Команда echoвызывает write()/ writev()конечно.

Итак, что происходит, это:

  • command 1>file.txt 2>&1создает только одно описание файла, потому что оболочка открывает файл только один раз. Оболочка заставляет как стандартные выходные данные, так и стандартные дескрипторы файла ошибок сопоставляться с этим описанием одного файла. Он дублирует стандартный вывод на стандартную ошибку. Таким образом, запись через любой дескриптор файла будет перемещать общую текущую позицию файла: каждая запись идет после предыдущей записи общего описания файла. И как видите результаты echoкоманд не перезаписывают друг друга.
  • command 1>file.txt 2>file.txtсоздает два описания файлов, поскольку оболочка открывает один и тот же файл дважды в ответ на два явных перенаправления. Стандартные выходные данные и стандартные дескрипторы файлов ошибок отображаются на два разных описания файлов, которые затем, в свою очередь, отображаются на один и тот же файл. Два описания файлов имеют совершенно независимые текущие позиции файлов, и каждая запись идет сразу же после предыдущей записи в том же описании файла. И, как вы можете видеть, результатом является то, что то, что написано одним, может перезаписывать то, что пишется другим, различными способами в зависимости от того, в каком порядке вы выполняете запись.

дальнейшее чтение

JdeBP
источник
1
Должно быть открытое описание файла, а не описание файла . Это больше о записи того, как файл был открыт больше, чем сам файл. Это терминология, используемая, по крайней мере, в документации POSIX, Linux, Solaris и GNU.
Стефан
16

Использование >говорит ему перезаписать файл. Так как stdout и stderr записывают в файл в двух разных операциях, последняя из них перезаписывает первую.

Ты можешь сделать:

command 1>>file.txt 2>>file.txt

или

command &>file.txt Только bash v4 и выше.

>> говорит ему добавить файл, чтобы он не заменил вывод предыдущих операций.

&> это просто более простой способ написать 2>&1

Jesse_b
источник
2
почему ls 1>&0и ls 0>&0до сих пор показывает вывод ls?
Yvain
Я удивлен, что использование >>работает. Почему в этом нет проблемы двух описаний файлов с независимыми смещениями? @JdeBP, ты знаешь? Я подумал, что открытие файла в режиме добавления эквивалентно открытию в режиме записи, поиску в конечной позиции, а затем запрету дальнейшего поиска.
JoL
4
@jlmg: Файлы в режиме добавления доступны для поиска, но каждая запись начинается с подразумеваемого поиска до конца. Является ли этот подразумеваемый поиск атомарным, мне менее ясно.
Кевин
1
Это полностью зависит от последующего вопроса. Такие, как эти, указывают на то, что ответ неполон. И вряд ли люди будут искать последующие вопросы, не желая также знать полный ответ. Следовательно, вполне вероятно, что такие последующие вопросы будут иметь ответы, которые дублируют информацию, и, таким образом, будут закрыты как дубликаты.
trlkly
1
@Kevin, в полностью POSIX-совместимых файловых системах неявный поиск O_APPEND является атомарным. Тем не менее, не каждая файловая система реализует соответствующую семантику должным образом - например, NFS (по крайней мере v3 и более ранние - у меня не ясно, v4) не имеет соответствующей поддержки, встроенной в проводной протокол, поэтому сервер имеет нет способа узнать, открыл ли клиент файл с O_APPEND.
Чарльз Даффи