Це помилка в розділі перенаправлення керівництва Баша?


13
Note that the order of redirections is significant.  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 duplicated from the standard output before the standard
   output was redirected to dirlist.

Тепер ця остання частина мене бентежить. У цьому випадку будь-яка стандартна помилка буде надрукована на термінал, і будь-яка STDOUT перейде до файлу dirlist. Це було б, але це не так, як я розумію посібник.

Схоже, слід сказати, "оскільки стандартна помилка була дублюється зі стандартного виводу ПІСЛЯ стандартного виводу був перенаправлений на dirlist". Якщо STDERR було надіслано до STDOUT до того, як STDOUT був спрямований у файл, то чи не міститиме цей файл STDOUT AND STDERR?

Може хтось, будь-ласка, очистить це для мене? Це просто погане розуміння читання з мого боку? Вживання дублювання слова здається мені трохи дивним у цьому контексті. Можливо, це мене кидає.



1
Класичний випадок змішування операцій, які "за значенням" проти "за посиланням". Коли ви дублюєте дескриптор файлу, це операція за значенням . У програмуванні після того, як a = 1; b = a; a = 2ви розраховуєте, a == 2 && b == 1що це правда. Перенаправлення 2>&1схоже на b = aпризначення - воно за значенням, а не за посиланням. 2>&1не вказує дескриптор файлів 2 на дескриптор файлу 1 на всю вічність - вони все ще є двома різними дескрипторами файлів, які вказують на один і той же файл.
jw013

Відповіді:


23

Дублювання справді є важливою складовою.

Подивимось, куди збираються дескриптори файлів перед перенаправленням. Це звичайно поточний термінал, наприклад:

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

Тепер, якщо ми дзвонимо ls -lбез перенаправлення, вихідні повідомлення та повідомлення про помилки переходять до мого терміналу під /dev/pts/1.

Якщо ми спочатку переспрямовуємо STDOUTфайл у файл ( ls -l > dirlist), він виглядає приблизно так:

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

Коли ми потім перенаправити STDERRдо дублікату з STDOUTфайлового дескриптора «S ( ls -l > dirlist 2>&1), STDERRйде до дублікату /home/bon/dirlist:

STDOUT ---> /home/bon/dirlist
STDERR ---> /home/bon/dirlist

Якщо ми спочатку переспрямовуємо STDERRна дублікат STDOUTфайлу дескриптора ( ls -l 2>&1):

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

і тоді STDOUT до файлу ( ls -l 2>&1 > dirlist) ми отримаємо це:

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

Ось, STDERRще збирається до терміналу.

Розумієте, порядок на сторінці чоловіка правильний.


Перенаправлення тестування

Тепер ви можете перевірити це самостійно. Використовуючи ls -l /proc/$$/fd/, ви бачите, куди STDOUT(з fd 1) та STDERR(з fd 2) збираються поточний процес:

$ ls -l /proc/$$/fd/
total 0
lrwx------ 1 bon bon 64 Jul 24 18:19 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 07:41 2 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 255 -> /dev/pts/1

Створимо невеликий скрипт оболонки, який показує, де вказані ваші дескриптори файлів. Таким чином, ми завжди отримуємо стан при виклику ls, включаючи будь-яке перенаправлення з оболонки виклику.

$ cat > lookfd.sh
#!/bin/sh
ls -l /proc/$$/fd/
^D
$ chmod +x lookfd.sh

CtrlD, ви надсилаєте кінець файлу і таким чином зупиняєте catзчитування команди STDIN.)

Тепер зателефонуйте цьому сценарію з різними комбінаціями перенаправлення:

$ ./lookfd.sh 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:08 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:08 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh 2>&1 > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out 2>&1
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:11 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:11 1 -> /home/bon/foo.out
l-wx------ 1 bon bon 64 Jul 24 19:11 2 -> /home/bon/foo.out
lr-x------ 1 bon bon 64 Jul 24 19:11 255 -> /home/bon/lookfd.sh

Ви можете бачити, що дескриптори файлів 1 (для STDOUT) та 2 (для STDERR) різняться. Для задоволення ви також можете перенаправити STDINта побачити результат:

$ ./lookfd.sh < /dev/zero
total 0
lr-x------ 1 bon bon 64 Jul 24 19:18 0 -> /dev/zero
lrwx------ 1 bon bon 64 Jul 24 19:18 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:18 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:18 255 -> /home/bon/lookfd.sh

(Залишене запитання читачеві: Де дескриптор файлу 255 балів? ;-))


+1 - відмінна відповідь. Надзвичайно добре написані та приголомшливі приклади. Дякую!!!
slm

Я бачу, я думаю, що моє непорозуміння полягало в тому, що перенаправлення буде стійким для всіх наступних команд, так що будь-який STDERR для решти рядка перейде до STDOUT.
Грегг Левенталь

2

Ні, керівництво правильно.

Якщо спочатку 1 вказує на термінал, а 2 також на термінал, тоді:

command  2>&1   1>somewhere

перенаправлення оцінювання буде відбуватися зліва направо.

Таким чином, він ПЕРШИЙ оцінить 2>&1, і, таким чином, ПЕРШИЙ скопіюйте те, 1на що вказував fd (тобто дескриптор файлу the terminal, як правило, / dev / tty), в fd 2.

Тож у цей момент fd 2тепер вказує на те, де fd 1вказував на ( the terminal)

А потім він оцінює 1>somewhereчастину, і таким чином скопіює дескриптор файлу somewhereв fd 1(так що в цей момент 1тепер fd вказує somewhere, а fd 2все ще вказує на the terminal)

Таким чином, він дійсно друкує 1 в "кудись" і 2 в термінал, оскільки 2 дублюється з 1 ДО ПЕРЕД 1 було змінено.

Інше замовлення:

command  1>somewhere 2>&1

спочатку буде перенаправляти fd 1на somewhere, а потім скопіювати цю саму посилання у fd 2, тому в кінці 2 також вказує на somewhere. Але вони відтепер не "пов'язані". Кожен можна переадресувати окремо.

колишній:

command  1>somewhere 2>&1
exec 2>/dev/null

В кінці цього, fd 1вказує на somewhere, а fd 2спрямований на/dev/null

Звичайні назви для fd 1- STDOUT (стандартний вихід), а звичайна назва fd 2- STDERR (стандартна помилка, оскільки вона зазвичай використовується для відображення помилок, не заважаючи STDOUT)


@ Michael-mrozek: дякую за редагування, але я наполягаю на тому, щоб сказати "копіювати" замість "дубліката", оскільки "дублікат" може привести до того, що обидва - це "те саме", що не відповідає дійсності. ex:: cmd 1>somewhere 2>&1 ; exec 2>/dev/nullпісля exec лише 2 було переспрямовано на / dev / null (1 все ще збирається "кудись"). Мені потрібна допомога, щоб придумати спосіб сказати "на що 1 бал" замість "fd 1", однак ... як це теж заплутано ...
Олів'є Дулак

1
Я не впевнений, що ти маєш на увазі; ти той, хто змінив її з "копії" на "дублікат". Все, що я робив, - це використання великої літери та форматування речей, я не змінив слова
Michael Mrozek

дох ... ^^ вибачте. І я знову відредагував, щоб переформулювати, щоб зробити точніше, що скопійовано в те, що ^^
Олів'є Дулак

1

Я думаю, що тут заплутаною частиною є неправильне розуміння того, що перенаправлення stderr на stdout насправді з'єднує два потоки.

Ідеально розумна ідея, але те, що трапляється, коли ви пишете 2>&1stderr, забирає пекабу на те, що пише stdout і пише саме там же. Тому якщо згодом ви скажете stdout піти писати кудись інше, це не вплине на призначення stderr, яке вже переміщено.

Я думаю, що це сам контрінтуїтив, але це так працює. Установіть, куди ви хочете написати спочатку, а потім скажіть усім "скопіюйте мене". Сподіваюся, що з'ясовується ...


0

ДУПЛІКАЦІЯ ...

важливо, але скоріше в тому сенсі, що воно є джерелом великої плутанини . Це дійсно досить просто. Ця відповідь є лише «радикальною» ілюстрацією.

Прийнята відповідь хороша, але занадто довга і підкреслює «дублювання».

Q розумно закінчується на:

Використання дублювання слів здається трохи дивним для мене в цьому контексті. Можливо, це мене кидає.

Я використовую позначення bash і визначаю змінні "один" і "два" як файлові файли "1" і "2". Оператор переадресації (вихід) >- це призначення =. &і $означають "значення".

Приклади man bash (додано "1" за замовчуванням)

ls 1>dirlist 2>&1      # both to dirlist
ls 2>&1 1>dirlist      # 1 to dirlist, 2 stays on tty/screen 

стати:

one=dirlist  two=$one

і

two=$one   one=dirlist

І навіть це для мене неавтоматично, і деякі інші я здогадуюсь. Перший рядок залишає вас $oneі $twoобидва містять «DirList». Звичайно.

Другий рядок починається з марного призначення. Обидва починаються за визначенням з "TTY" (трохи символічно) як їх напрямок ; це призначення не змінюється жодним значенням, а зі змінними, як у файлових файлах, нічого не пов'язане магічно. На змінну twoне впливає наступне one=dirlist. Звичайно, ні.

Хтось тут (6 років тому) запропонував "вказати на" замість "копіювати" або "дублювати", а потім зрозумів: це теж буде заплутано.

Це дублювання чи показовий семантичний навіть не потрібні. Можливо, саме амперсанд потребує більшої уваги. "Значення" оператора / маркера / будь-якого іншого.

Якщо - і лише якщо - ви шукаєте спосіб отримати дивовижний номер роботи на своїй консолі , тоді повідомлення "зроблено" плюс як бонус файл з назвою "2", тоді ви переходите:

ls 1>2& 2>/dev/null

Зчитується, природно, як " копіювати" / "дублювати" 1 до 2, а потім обидва разом нулю . Але думка неправильна, а також синтаксис. (але немає синтаксичної помилки, вона дійсна)

Правильний спосіб планувати це - перенаправити будь-яке з двох на нуль, а потім перенаправити ІНШЕ на місце ІНШЕ:

ls 1>/dev/null 2>&1
# or 
ls 2>/dev/null 1>&2

(провідний "1" можна залишити)

(Гаразд відповідно до а. Не надто довгий, але це занадто багато списку - або: дуже хороша візуалізація, не дуже гарне пояснення)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.