Подтвердить что ты не робот

Порядок ввода/вывода

Я играю с перенаправлением оболочки i/o. Команды, которые я пробовал (в bash):

ls -al *.xyz 2>&1 1> files.lst

и

ls -al *.xyz 1> files.lst 2>&1

В текущей папке нет файла *.xyz.

Эти команды дают мне разные результаты. Первая команда показывает сообщение об ошибке ls: *.xyz: No such file or directory на экране. Но второй печатает это сообщение об ошибке в файле. Почему первая команда не смогла записать вывод ошибки в файл?

4b9b3361

Ответ 1

Эта ошибка:

ls: *.xyz: No such file or directory

записывается на stderr на ls двоичный.

Однако в этой команде:

ls -al *.xyz 2>&1 1> files.lst

Сначала вы перенаправляете stderr в stdout, который по умолчанию переходит на tty (terminal)

И затем вы перенаправляете stdout в файл files.lst, однако помните, что stderr не перенаправляется в файл, поскольку у вас есть stderr до stdout перенаправление до stdout to file. В этом случае ваш stderr все равно будет записан в tty.

Однако во втором случае вы меняете порядок перенаправления (сначала stdout на file, а затем stderr на stdout) и это правильно переадресует stderr на file, который также используется stdout.

Ответ 2

Bash manual имеет ясный пример (аналогичный вашему), чтобы показать, что порядок имеет значение, а также объясняет разницу. Здесь была взята соответствующая часть (выделено мной):

Обратите внимание, что порядок перенаправления значителен. Например, Команда

ls > dirlist 2 > & 1

направляет как стандартный вывод (дескриптор файла 1), так и стандартную ошибку (дескриптор файла 2) в файл dirlist, а команда

ls 2 > & 1 > dirlist

направляет только стандартный вывод в файл dirlist, потому что стандарт ошибка была сделана копией стандартного вывода перед стандартом выход был перенаправлен на dirlist.

Этот пост объясняет это с точки зрения POSIX.

Конфузии происходят из-за ключевой разницы. > перенаправляет, не делая левый операнд (stderr) указывать на правый операнд (stdout), но сделав копию правильного операнда и назначив его влево. Концептуально присваивание копией, а не ссылкой.

Итак, чтение слева направо, как это интерпретируется Bash: ls > dirlist 2>&1 означает перенаправление stdout в файл dirlist, за которым следует перенаправление stderr на все stdout в настоящее время указывая на (который уже является файлом). OTOH ls 2>&1 > dirlist перенаправляет stderr на то, что в настоящее время указывает stdout (это экран/терминал), а затем перенаправляет stdout в dirlist.

Ответ 3

Перенаправления:

  • обрабатывается слева направо.
  • интерпретируется итеративно:
    • предыдущее перенаправление может повлиять на более поздний:
      • если ранее перенаправление перенаправило данный поток (идентифицированный номером дескриптора файла, например 1 для stdout (по умолчанию) и 2 для stderr), последующие перенаправления, предназначенные для этого потока, относятся к уже существующему потоку, перенаправленная версия.
    • но не наоборот - последующее перенаправление не оказывает обратного действия на цель предыдущего перенаправления:
      • например, если вы укажете файловый дескриптор 1 как цель в более раннем перенаправлении, то что 1 означает, что в это время блокируется, даже если 1 перенаправляется позже.
  • Обратите внимание, однако, что вывод фактически не отправляется до тех пор, пока все перенаправления не будут установлены, и что любые выходные файлы перенаправления создаются или усекаются до начала выполнения команды (это причина, по которой вы не можете читать и перенаправить вывод в тот же файл с помощью одной команды).

Применяется к примеру из вопроса:

  • >file 2>&1:

    • >file сначала перенаправляет stdout (дескриптор файла 1, подразумеваемый не префиксом > с номером дескриптора файла) для вывода файла file
    • 2>&1 затем перенаправляет stderr (2) на уже перенаправленный stdout (1).
    • Чистый эффект заключается в том, что оба исходных потока заканчиваются на file.
  • 2>&1 >file:

    • 2>&1 сначала перенаправляет stderr на исходный stdout; поскольку файловый дескриптор 2 участвует в отсутствии дальнейших перенаправлений, вывод stderr будет идти к тому, что было определено stdout как в этой точке, то есть исходное stdout, учитывая, что это первое перенаправление.
      • Технически, оригинальный дескриптор файла stdout дублируется, и этот дубликат - это то, что stderr ссылается на него, что объясняет, почему на него не влияет последующее перенаправление stdout.
    • >file затем перенаправляет исходный stdout на file - но это больше не влияет на уже заблокированное перенаправление stderr.
    • Чистый эффект заключается в том, что только вывод sent-direct-to-stdout записывается в file, в то время как вывод send-to-stderr выводится на (исходный, unredirected) stdout.

Ответ 4

Потому что порядок имеет значение. В первом случае вы сначала перенаправляете stderr (2) на stdout (1). Затем вы перенаправляете (1) в файл. Но stderr (2) по-прежнему указывает на стандартный вывод оболочки, запускающей команду. Указание (1) на файл в этом случае не изменяет устройство вывода, на которое (2) направлено, поэтому оно все равно переходит к терминалу.

Во втором случае вы перенаправляете stdout (1) в файл. Затем вы указываете stderr (2) на то же место, на которое указывает 1, который является файлом, поэтому сообщение об ошибке переходит к файлу.