Безопасно ли перенаправлять 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

Который, кажется, работает. Есть ли опасность для этого подхода?

exit_status
источник
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.

mosvy
источник
3
Формально для файлов в режиме добавления это безопасно . Упомянутая проблема - это ошибка в NFS, которая делает ее непригодной (не соответствующей POSIX) в качестве файловой системы. Для случая без добавления режима это никогда не безопасно.
Р ..
1
Это несущественно. Дважды добавьте OP является не безопасным для использования (в дополнение к тому , совершенно бессмысленно). И O_APPENDв любом случае это своего рода обман - довольно обременительно для правильной реализации.
Мосвы
Я считаю, что состояние гонки NFS только между разными клиентами. Клиентская ОС должна координировать все записи между своими процессами.
Бармар
@ Barmar, это было бы верно, если бы клиентская ОС заботилась только о своем собственном представлении файла nfs. Но при записи в файл nfs, открытый с помощью O_APPEND, клиент сначала извлекает «реальный» размер файла с сервера («повторно проверяет» индекс), а затем выполняет обновление индексируемого поиска + запись +, и только последняя часть сделано под блокировками, что означает, что первая часть может все еще получить устаревший размер с сервера и переопределить правильный размер из локального / кэшированного индекса. Та же проблема с 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 еще не был изобретен). Даже на Солярисе 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
Да, это будет по-другому. Как я уже сказал, это зависит от вашей цели >- перезаписать. >>- добавь
Ангел