2013-07-31 3 views
14

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

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

и

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

Там нет какого-либо *.xyz файла в текущей папке.

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

ответ

8

Эта ошибка:

ls: *.xyz: No such file or directory 

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

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

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

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

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

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

+10

Stdout НЕ по умолчанию tty. Он просто унаследован от оболочки. Если оболочка является интерактивной и запущена на tty, то stdout и stderr по умолчанию - tty. Если оболочка не запущена на tty, то это не так. Я понимаю, что это похоже на нит, но важно не связывать stdout с терминалом, и люди делают эту ошибку очень часто. –

+3

Да, я имел в виду в контексте интерактивной оболочки, так как OP запускает эту команду в интерактивной оболочке. – anubhava

2

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

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

+3

В основном правильно, но я сомневаюсь в формулировке четвертого предложения «Но stderr по-прежнему указывается на stdout». После того, как fd 1 перенаправлен в файл, fd 2 все еще привязан к файлу (tty - это файл), что stdout оболочки связанный с, но не с командой команды. Возможно, измените это предложение на чтение: 'stderr по-прежнему указывает на stdout оболочки, на которой запущена команда'. Что еще более важно, не объединяйте stdout с терминалом. Это часто терминал, но (вероятно, чаще) нет. –

+0

@WilliamPursell Согласен, я сделал ваше редактирование. Вероятно, это было разумно в этом конкретном случае, но не было хорошего общего ответа. – user1676075

22

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

Note that the order of redirections is significant. For example, the command

ls >dirlist 2>&1

directs both standard output (file descriptor 1) and standard error (file descriptor 2) to the file dirlist, while the command

ls 2>&1 >dirlist

directs only the standard output to file dirlist, because the standard error was made a copy of the standard output before the standard output was redirected to dirlist.

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

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

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

12

Перенаправление являются:

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

Применительно к примеру с вопроса:

  • >file 2>&1:

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

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