Чи переадресація з `>>` еквівалентна `>`, коли цільовий файл ще не існує?


80

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

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

Якщо файл не існує, він створюється з нульовим розміром; потім написано в. Це справедливо для обох операторів. Може здатися, що оператори еквівалентні, коли цільовий файл ще не існує.

Вони насправді?

Відповіді:


107

тл; д-р

Ні >>, по суті, "завжди прагнути до кінця файлу", >зберігаючи вказівник на останнє записане місце.


Повна відповідь

(Примітка: всі мої тести, зроблені на Debian GNU / Linux 9).

Ще одна відмінність

Ні, вони не рівноцінні. Є ще одна відмінність. Він може проявлятися незалежно від того, існував цільовий файл раніше чи ні.

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

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

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

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

Приклад

Це має значення на практиці? Є таке питання :

Я запускаю процес, який дає багато результатів у stdout. Надсилаючи все це до файлу [...] Чи можу я використовувати якусь програму обертання журналу?

Ця відповідь говорить, що рішення є logrotateз copytruncateваріантом, який діє так:

Обрізайте оригінальний файл журналу на місці після створення копії, замість переміщення старого файлу журналу та необов'язково створення нового.

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

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

У цьому випадку використання >>замість цього >значно краще, навіть якщо цільовий файл ще збирається створити.


Продуктивність

Як ми бачимо, два оператори діють по-різному не тільки тоді, коли вони починаються, але і пізніше. Це може спричинити деяку (тонку?) Різницю у виконанні. На даний момент у мене немає значущих результатів тесту, які б їх підтримували або спростовували, але я думаю, ви не повинні автоматично вважати, що їх продуктивність є загальною.


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

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

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

2
@ilkkachu я знайшов , що це становить інтерес, так як вона пояснює подробиці про O_APPEND , який я задавався питанням про те, після вашого коментаря :): stackoverflow.com/questions/1154446 / ...
jjmontes

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