Чи безпечно перенаправляти stdout та stderr до одного файлу без копій дескриптора файлу?


27

Я починаю з порожнього каталогу.

$ touch aFile
$ ls
aFile

Тоді я lsдва аргументи, жодного з яких немає в цьому каталозі. Я перенаправляю обидва вихідні потоки у файл з назвою output. Я використовую >>, щоб не писати одночасно.

$ ls aFile not_exist >>output 2>>output
$ cat output
ls: cannot access 'not_exist': No such file or directory
aFile

Який, здається, працює. Чи є небезпека такого підходу?


6
Це було швидке голосування проти. Минуло як п’ять секунд. Чи можете ви сказати мені, як ви можете так швидко оцінити гідність мого питання? І ще краще, що з цим не так, щоб я міг його покращити?
exit_status

Чому ви тут не використовуєте більш стандартний ls aFile not_exist &>>output? (Зверніть увагу, я припускаю, що ви використовуєте bash .)
FedonKadifeli

5
Тому що це не допомагає мені зрозуміти, про що я прошу. Я знаю, як перенаправити ці потоки в один і той же файл, портативно навіть. Що я хочу знати, це якщо щось не так з тим, що я запропонував у питанні. @FedonKadifeli
exit_status

1
@FedonKadifeli &>>НЕ стандартний. Це ОБЛАДНАНИЙ, неоднозначний синтаксис, який по-різному працює в різних оболонках. Цікаво, звідки ви, хлопці, берете свої речі.
Дядько Біллі

4
Баш - це не стандарт . Стандартні мандати POSIX , які ls &>>foo ...повинні бути розібрані як два comands ls &і >>foo ..., і це шлях , інші оболонки на кшталт /bin/shз Ubuntu розбирає його. Оскільки це застаріло, ви можете подивитися тут - хоча я не претендую на те, що це якась влада. Ви можете запитати у bashтехнічних працівників, якщо вони вважають, що це корисна ідея.
Дядько Біллі

Відповіді:


22

Ні, це не так безпечно, як стандарт >>bar 2>&1.

Коли ти пишеш

foo >>bar 2>>bar

Ви відкриваєте barфайл двічі O_APPEND, створюючи два абсолютно незалежних файлових об'єкта [1], кожен з яких має свій стан (вказівник, відкриті режими тощо).

Це дуже не на відміну від того, 2>&1що просто викликає dup(2)системний виклик, і робить stderr і stdout псевдонімами, що змінюються, для одного і того ж файлового об’єкта.

Тепер у цьому є проблема:

O_APPENDможе призвести до пошкодження файлів у файлових системах NFS, якщо більше одного процесу додає дані до файлу одночасно. Це тому, що NFS не підтримує додавання до файлу, тому ядро ​​клієнта має імітувати його, що неможливо зробити без умови перегону.

Зазвичай ви можете розраховувати на ймовірності файлу , як barв foo >>bar 2>&1записуються одночасно з двох різних місць , які є досить низькими. Але своїм >>bar 2>>barви просто збільшили його на десяток порядків, без будь-яких причин.

[1] "Відкрити описи файлів" в мові POSIX.


3
Формально для файлів у режимі додавання це безпечно . Цитується проблема - це помилка в NFS, яка робить її непридатною (не відповідає POSIX) як файловій системі. Для випадку, що не додається, він ніколи не є безпечним.
Р ..

1
Це несуттєво. Подвійне додавання ОП не є безпечним у використанні (крім того, що абсолютно безглуздо). І все-таки O_APPENDє різновидом ботчу - досить обтяжливим, щоб правильно реалізувати.
москва

Я вважаю, що стан перегонів NFS є лише між різними клієнтами. Клієнтська ОС повинна координувати всі записи між своїми процесами.
Вармар

@Barmar, це було б правдою, якби клієнтська ОС дбала лише про власний перегляд файлу nfs. Але при записі до файлу nfs, відкритого за допомогою O_APPEND, клієнт спочатку отримує "реальний" розмір файлу з сервера ("перезавантажує" inode), а потім виконує пошук + запит + кешоване оновлення inode, і лише остання частина - зроблено під блокуваннями, а це означає, що перша частина все-таки зможе отримати нескладний розмір із сервера та замінити правильний з локального / кешованого inode. Така ж проблема і з lseek(SEEK_END).
москва

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

22

Що відбувається, коли ти робиш

some_command >>file 2>>file

є те, що fileбуде відкрито для додавання двічі. Це безпечно робити у файловій системі POSIX. Будь-яке записування, яке трапляється у файл під час відкриття для додавання, відбуватиметься в кінці файлу, незалежно від того, надходять дані над стандартним вихідним потоком або стандартним потоком помилок.

Це спирається на підтримку операцій запису атомного додавання в базовій файловій системі. Деякі файлові системи, такі як NFS, не підтримують атомне додавання. Дивіться, наприклад, питання "Чи додається файл атомним в UNIX?" На StackOverflow.

Використання

some_command >>file 2>&1

хоч би працював навіть на NFS.

Однак, використовуючи

some_command >file 2>file

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

Приклад:

$ { echo hello; echo abc >&2; } >file 2>file
$ cat file
abc
o

helloРядок записується першим (з кінцевим нового рядка), а потім рядок з abcподальшим переведенням рядка записується зі стандартної помилки, перезапису hell. В результаті виходить рядок abcз новим рядком, після чого слід залишити перший echoвихід, а oта новий рядок.

Заміни двох echoнавколо рани виробляються лише helloу вихідному файлі, оскільки ця рядок пишеться останньою та довшою за abcрядок. Порядок, в якому відбуваються переадресації, значення не має.

Краще і безпечніше було б використовувати більш ідіоматичне

some_command >file 2>&1

1
Незважаючи на те, що це стосується сучасних оболонок, це не було в оболонці Борна або Томсона (звідки >>походить), де можна >>було б відкрити для написання і прагнути до кінця (я вважаю, тому що O_APPEND тоді ще не був винайдений). Навіть на Solaris 10 /bin/sh -c '(echo a; echo b >&2) >> file 2>> file; cat file'виводить b.
Стефан Шазелас

@ StéphaneChazelas Це проблема з реалізацією Solaris 10 shабо її файловою системою?
Кусалаланда

1
Це те, що >>спочатку робилося, воно не відкривалося з O_APPEND, воно відкривалося без і прагнуло до кінця. Це не стільки питання, це те, що він робив, і це було задокументовано.
Стефан Шазелас

0

Це залежить того, чого ви хочете досягти. Ви вирішуєте, чи гаразд помилки у тому ж файлі, що і у вихідному. Це просто збереження тексту у файлі з функціональністю оболонки, яка дозволяє перенаправляти за бажанням. Не існує абсолютного «так» або «ні». Оскільки у Linux це можна зробити декількома способами, це мій спосіб ls notExistingFile existingFile >> output 2>&1 відповісти на питання: З точки зору самого перенаправлення, так, це абсолютно безпечно.


Тут є більше, ніж те, що ви тут говорите. Ця ж вправа, а не >замість >>, замінить деякі символи. Тож справа не лише в тому, що оболонка дозволяє мені переадресовувати, адже коли я переспрямовую >, результат є іншим. Так що є нюанси >, чи є >>?
exit_status

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