Чи> & - ефективніше, ніж> / dev / null?


58

Вчора я прочитав цей коментар ТА, який говорить про те, що в оболонці (принаймні bash) >&-"є такий самий результат, що і" >/dev/null.

Цей коментар насправді посилається на посібник ABS як джерело його інформації. Але це джерело говорить, що >&-синтаксис "закриває дескриптори файлів".

Мені не ясно, чи дві дії закриття дескриптора файлу та перенаправлення його на нульовий пристрій абсолютно рівнозначні. Тож моє запитання: вони?

На його поверхні здається, що закрити дескриптор - це як закрити двері, але перенаправити її на нульовий пристрій - це відкрити двері до кінцівки! Двоє мені не здаються абсолютно однаковими, тому що якщо я побачу закриту двері, я нічого не намагаюся викинути з неї, але якщо я побачу відкриту двері, я припускаю, що можу.

Іншими словами, я завжди замислювався про те, чи є >/dev/nullзасоби, які cat mybigfile >/dev/nullнасправді оброблять кожен байт файлу і записують його, /dev/nullякий він забуває. З іншого боку, якщо оболонка стикається із закритим дескриптором файлів, я схильна вважати (але не впевнений), що вона просто нічого не запише, хоча залишається питання, чи catвсе ще читатиме кожен байт.

Цей коментар говорить >&-і >/dev/null" повинен " бути однаковим, але це не настільки гучна відповідь для мене. Я хотів би отримати більш авторитетну відповідь із деяким посиланням на стандартне або вихідне ядро ​​чи ні ...


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

Відповіді:


71

Ні, ви точно не хочете закривати дескриптори файлів 0, 1 і 2.

Якщо ви зробите це, коли програма відкриє файл, коли він стане stdin / stdout / stderr ...

Наприклад, якщо ви робите:

echo text | tee file >&-

Коли tee(принаймні деякі реалізації, як-от зайнятий ') відкриється файл для запису, він буде відкритий у дескрипторі 1 файлу (stdout). Тож teeнапишемо textдвічі file:

$ echo text | strace tee file >&-
[...]
open("file", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 1
read(0, "text\n", 8193)                 = 5
write(1, "text\n", 5)                   = 5
write(1, "text\n", 5)                   = 5
read(0, "", 8193)                       = 0
exit_group(0)                           = ?

Це, як відомо, викликає вразливості безпеки. Наприклад:

chsh 2>&-

І chsh(застосований додаток) може записати повідомлення про помилки в /etc/passwd.

Деякі інструменти і навіть деякі бібліотеки намагаються захистити від цього. Наприклад GNU teeпереміщує файловий дескриптор до одного вище 2 , якщо файли , які він відкриває для запису присвоюється 0, 1, 2 , а BusyBox teeне буде.

Більшість інструментів, якщо вони не можуть записати в stdout (оскільки, наприклад, воно не відкрите), повідомлять про повідомлення про помилку на stderr (мовою користувача, що означає додаткову обробку для відкриття та розбору файлів локалізації ...), так це буде значно менш ефективно, і, можливо, призведе до відмови програми.

У будь-якому випадку це не буде більш ефективним. Програма все ще буде робити write()системний виклик. Це може бути більш ефективним, лише якщо програма відмовиться від написання на stdout / stderr після першого невдалого write()системного виклику, але програми, як правило, цього не роблять. Зазвичай вони або виходять із помилкою, або продовжують намагатися.


4
Я думаю, що ця відповідь була б ще кращою, якби останній абзац був у верхній частині (оскільки саме це найбільш безпосередньо відповідає на питання ОП), і далі він обговорив, чому це погана ідея, навіть якщо вона здебільшого спрацьовує. Але я візьму його, мати прихильність. ;)
CVn

@ StéphaneChazelas: Як сказав Майкл, я очікував би останнього пункту на вершині, але спасибі за уточнення це насправді, ймовірно, лише створює проблеми. Тож я думаю, що допоміжним питанням буде, коли закриття ФД насправді стане корисним? Або я мушу задати це окремим питанням?
jamadagni

@jamadagni, див. unix.stackexchange.com/search?q=user%3A22565+%223%3E%26-%22 для деяких прикладів
Stéphane Chazelas

1
@jamadagni Якщо посилання, надане Stéphane, не відповідає на питання, я б сказав, що це звучить як початок окремого запитання, оскільки це не має прямого відношення до відносної ефективності двох методів.
CVn

1
Я розумію, що Стефан починає з цього важливого попередження безпеки, оскільки це було б менш видно, якби останній абзац був зверху. +1 від мене.
Олів'є Дулак,

14

IOW я завжди замислювався, чи >/dev/nullозначає, що cat mybigfile >/dev/nullнасправді оброблятиме кожен байт файлу і записує його до того, /dev/nullщо він його забуває.

Це не повна відповідь на ваше запитання, але так, вище, як це працює.

catчитає названі файли (файли) або стандартний вхід, якщо жодні файли не названі, і виводить на його стандартний висновок вміст тих, доки він не зустріне EOF на (включаючи стандартний ввід) останній названий файл. Це його робота.

Додавши, >/dev/nullви перенаправляєте стандартний вихід на / dev / null. Це спеціальний файл (вузол пристрою), який викидає все, що в ньому написано (і негайно повертає EOF на прочитане). Зауважте, що перенаправлення вводу / виводу - це функція, що надається оболонкою, а не кожною окремою програмою, і що немає нічого магічного щодо імені / dev / null, лише те, що відбувається там, у більшості подібних Unix систем .

Важливо також зазначити, що конкретна механіка вузлів пристрою відрізняється від операційної системи до операційної системи, але кішка (яка в системі GNU означає coreutils) є кросплатформою (той самий вихідний код повинен працювати принаймні на Linux і Hurd) і, отже, не може залежати від конкретних ядер операційної системи. Крім того, він все ще працює, якщо ви створюєте псевдонім / dev / null (в Linux це означає вузол пристрою з тим же основним / другорядним номером пристрою) з іншим ім'ям. І завжди є випадки написання десь в іншому місці, яке діє так само (скажімо, / dev / zero).

Звідси випливає, що catне знає про особливі властивості / dev / null, і, ймовірно, в першу чергу не знає про переспрямовування, але йому все одно потрібно виконати точно таку ж роботу: він читає названі файли та виводить вміст / ті файли до його стандартного виводу. Те, що стандартний вихід випадків catпереходить у порожнечу, не є catсамим собою.


2
Щоб розширити свою відповідь: Так, cat mybigfile > /dev/nullзмусить catпрочитати кожен байт bigfileпам'яті. І для кожного nбайта він зателефонує write(1, buffer, n). Невідомий catпрограмі, writeволя не зробить абсолютно нічого (крім, можливо, для якоїсь тривіальної бухгалтерії). Для запису /dev/nullне потрібно обробляти кожен байт.
G-Man

2
Пам’ятаю, мене здуло, коли я читав джерело ядра Linux пристрою / dev / null. Я очікував, що там буде якась досконала система вільних () ing буферів тощо, але, ні, це в основному лише return ().
Брайан Мінтон

4
@ G-Man: Я не знаю, що ви можете гарантувати, що це правда у всіх випадках. Зараз я не можу знайти доказів, але я пригадую, що деяка реалізація того catчи cpіншого могла б спрацювати, mmapввівши в пам'ять великі шматки вихідного файлу, а потім зателефонувавши write()у відображений регіон. Якби ви писали до цього /dev/null, write()дзвінок одразу повернеться без помилок на сторінках вихідного файлу, тому він ніколи насправді не читатиметься з диска.
Нейт Елдредж

2
Крім того, щось на зразок GNU catпрацює на багатьох платформах, але випадковий погляд на вихідний код покаже багато #ifdefs: це не буквально той самий код, який працює на всіх платформах, і є безліч розділів, що залежать від системи.
Нейт Елдредж

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