Ви вже отримали кілька дуже хороших відповідей. Дозвольте наголосити, що тут задіяні дві різні концепції, розуміння яких надзвичайно допомагає:
Фон: Дескриптор файлу та файлова таблиця
Дескриптор вашого файлу - це просто число 0 ... n, що є індексом у таблиці дескрипторів файлів у вашому процесі. За умовою, STDIN = 0, STDOUT = 1, STDERR = 2 (зауважте, що терміни STDINтощо - це лише символи / макроси, що використовуються умовно в деяких мовах програмування та на сторінках man, не існує фактичного "об'єкта", який називається 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 (зазвичай, користувач).
Відповідь
Щоб застосувати наведену вище довідкову інформацію до свого питання:
У якому порядку оболонка виконує команди та перенаправляє потоки?
Зліва направо.
<command> > file.txt 2>&1
fork від нового процесу.
- Відкрийте
file.txtі збережіть його вказівник у дескрипторі файлів 1 (STDOUT).
- Вкажіть STDERR (дескриптор файлу 2) на те, на що зараз вказує fd 1 (що знову ж таки є вже відкритим
file.txt).
exec то <command>
Це, мабуть, перенаправляє stderr спочатку до stdout, а потім отриманий stdout переспрямовується на file.txt.
Це мало б сенс, якби була лише одна таблиця, але, як пояснено вище, є дві. Дескриптори файлів не вказують один на одного рекурсивно, немає сенсу думати "перенаправляти STDERR на STDOUT". Правильна думка - "вкажіть STDERR туди, куди точки STDOUT". Якщо пізніше ви зміните STDOUT, STDERR залишається там, де він є, він не магічно йде разом з подальшими змінами на STDOUT.