Перенаправление с `>>` эквивалентно `>`, когда целевой файл еще не существует?


80

Рассмотрим оболочку типа Bash или sh. Основное различие между >и >>проявляется в случае, когда целевой файл существует:

  • > обрезает файл до нулевого размера, затем записывает;
  • >> не усекает, пишет (добавляет) в конец файла.

Если файл не существует, он создается с нулевым размером; затем написано Это верно для обоих операторов. Может показаться, что операторы эквивалентны, когда целевой файл еще не существует.

Они действительно?

Ответы:


107

ТЛ; др

По >>сути, «всегда ищет конец файла», в то время как >поддерживает указатель на последнее записанное местоположение.


Полный ответ

(Примечание: все мои тесты сделаны на Debian GNU / Linux 9).

Еще одно отличие

Нет, они не эквивалентны. Есть еще одно отличие. Он может проявляться независимо от того, существовал ли ранее целевой файл или нет.

Чтобы наблюдать это, запустите процесс, который генерирует данные и перенаправьте в файл с помощью >или >>(например pv -L 10k /dev/urandom > blob). Пусть он запустится и измените размер файла (например, с помощью truncate). Вы увидите, что >сохраняет свое (растущее) смещение, при этом >>всегда добавляясь к концу.

  • Если вы урежете файл до меньшего размера (это может быть нулевой размер)
    • >не волнует, он напишет с желаемым смещением, как будто ничего не произошло; сразу после того, как усечение смещения выходит за пределы конца файла, это приведет к тому, что файл вернется к своему старому размеру и будет расти дальше, пропущенные данные будут заполнены нулями (разреженным, если это возможно);
    • >> добавится в новый конец, файл вырастет из усеченного размера.
  • Если вы увеличите файл
    • >не волнует, он напишет с желаемым смещением, как будто ничего не произошло; просто после изменения размера смещение находится где-то внутри файла, это приведет к тому, что файл на некоторое время перестанет расти, пока смещение не достигнет нового конца, тогда файл будет расти нормально;
    • >> добавится в новый конец, файл увеличится в увеличенном размере.

Другой пример - добавить (с отдельным >>) что-то дополнительное, когда запущен процесс генерации данных и запись в файл. Это похоже на увеличение файла.

  • Процесс генерации с >запишет с желаемым смещением и перезапишет дополнительные данные в конце концов.
  • Процесс генерации с >>пропустит новые данные и добавит их после (может возникнуть условие гонки, два потока могут быть чередованы, но данные не должны быть перезаписаны).

пример

Имеет ли это значение на практике? Есть такой вопрос :

Я запускаю процесс, который выдает большой объем вывода на стандартный вывод. Отправка всего этого в файл [...] Могу ли я использовать какую-нибудь программу ротации журналов?

Этот ответ говорит, что решение logrotateс copytruncateопцией, которая действует так:

Усечение исходного файла журнала на месте после создания копии, вместо перемещения старого файла журнала и, при необходимости, создания нового.

Согласно тому, что я написал выше, перенаправление с помощью >сделает усеченный журнал большим в кратчайшие сроки. Разреженность спасет день, не нужно тратить значительное дисковое пространство. Тем не менее, в каждом последующем журнале будет все больше и больше ведущих нулей, которые совершенно не нужны.

Но если logrotateсоздавать копии без сохранения разреженности, этим ведущим нулям потребуется все больше и больше дискового пространства при каждом создании копии. Я не исследовал поведение инструмента, оно может быть достаточно умным с разреженностью или сжатием на лету (если сжатие включено). Тем не менее, нули могут только создавать проблемы или быть нейтральными в лучшем случае; ничего хорошего в них нет.

В этом случае использование >>вместо >значительно лучше, даже если целевой файл еще не создан.


Представление

Как мы видим, два оператора действуют по-разному не только в начале, но и позже. Это может вызвать некоторую (незначительную?) Разницу в производительности. Пока у меня нет значимых результатов тестов, чтобы поддержать или опровергнуть их, но я думаю, что вы не должны автоматически предполагать, что их производительность в целом одинакова.


9
По >>сути, это «всегда искать в конце файла», в то время как >поддерживает указатель на последнее записанное местоположение. Кажется, что в их работе может быть и
небольшая

10
На уровне системных вызовов >>использует O_APPENDфлаг дляopen() . И на самом деле, >использует O_TRUNC, а >>не делает. Комбинация O_TRUNC | O_APPENDтакже была бы возможна, язык оболочки просто не предоставляет эту функцию.
ilkkachu

3
@jjmontes, стандартным источником будет POSIX: pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/… но, конечно, руководство Bash также содержит описания операторов перенаправления, включая нестандартные, которые он поддерживает: gnu.org/ программное обеспечение / bash / manual / html_node / Redirections.html
ilkkachu

2
@ilkkachu Я нашел это интересным, так как он объясняет подробности об O_APPEND, о которых я спрашивал после вашего комментария :): stackoverflow.com/questions/1154446/…
jjmontes

1
@Mokubai, любая здравомыслящая ОС будет иметь длину файла под рукой, когда она открыта, и проверка флага и перемещение смещения в конец должны просто исчезнуть во всех остальных бухгалтериях. Попытка эмулировать O_APPENDс lseek()перед тем, как каждый из write()них будет отличаться, потребует дополнительных системных вызовов. (И, конечно, это не сработает, поскольку write()между ними может
пройти
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.