Чому перенаправлення виводу файлу до себе створює порожній файл?


19

Чому перенаправлення виводу файлу до себе створює порожній файл?

Заявлений у Bash, чому так

less foo.txt > foo.txt

і

fold foo.txt > foo.txt

виробляти порожнє foo.txt? Оскільки додаток, наприклад, less eggs.py >> eggs.pyстворює дві копії тексту у eggs.py, можна очікувати, що перезапис створить одну копію тексту.

Зауважте, я не кажу, що це помилка, це швидше вказівник на щось глибоке про Unix.


Відповіді:


20

При використанні >файл відкривається в режимі усікання, тому його вміст видаляється до того, як команда намагатиметься його прочитати.

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

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

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

    fold foo.txt > fold.txt.$$ && mv fold.txt.$$ foo.txt
  • Уникайте файлу-посередника за рахунок потенційної часткової або повної втрати даних у разі помилки чи переривання. У цьому прикладі вміст foo.txtпередається як вхід до підшару (всередині дужок) перед тим, як файл буде видалений. Попередній вхід залишається живим, оскільки підрозділ підтримує його відкритим під час читання даних. Файл, записаний внутрішньою утилітою (тут fold), маючи те саме ім'я (foo.txt) вказує на інший inode, оскільки старий запис каталогу був видалений настільки технічно, під час процесу є два різних "файли" з тим самим іменем. Коли закінчується нижня оболонка, старий ввід звільняється, а його дані втрачаються. Слідкуйте за тим, щоб у вас було достатньо місця для тимчасового зберігання як старого, так і нового файлу одночасно, інакше ви втратите дані.

    (rm foo.txt; fold > foo.txt) < foo.txt

3
spongeвід moreutils також може допомогти. fold foo.txt | sponge foo.txt- абоfold foo.txt | sponge !$ також слід робити.
slhck

@slhck Дійсно, губка теж може виконати роботу. Однак, не будучи визначеним ні POSIX, ні основним потоком в Unix, як ОС, навряд чи він присутній.
jlliagre

Це не так, що його неможливо зробити присутнім;)
slhck

7

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


0

У bash, оператор перенаправлення потоку ... > foo.txtспорожняється foo.txt перед оцінкою лівого операнда .

Можна використовувати підстановку команд і роздрукувати її результат як вирішення. Це рішення містить менше додаткових символів, ніж інші відповіді:

printf "%s\n" "$(less foo.txt)" > foo.txt

Остерігайтеся: Ця команда не зберігає жодних нових рядків у foo.txt. Перегляньте в розділі коментарів нижче для отримання додаткової інформації

Тут підшалл $(...)оцінюється перед оператором перенаправлення потоку> , отже, збереження інформації.


@KamilMaciorowski: Насправді є tmp=$(cmd; printf q);  printf '%s' "${tmp%q}". Але ви пропустили ще одне питання з цією відповіддю: в ньому йдеться про «підзарядку», коли це означає «підміна команд». Так, заміни команд - це, як правило, підзаголовки, але не навпаки, і підзаголовки, як правило, не допомагають цій проблемі.
Скотт

@KamilMaciorowski Я так погано відчуваю, що пропускаю все це. Дякуємо, що вказали на все це. Для вашого (4-го) пункту: чи будуть зворотні котирування робити трюк, тобто зберігати відсталий новий рядок?
Луї-Якоб Лебель

@Scott дякую за вашу відповідь. Я змінив "subshell" на "substitution command". До речі, мені цікаво, яка точна різниця між ними.
Луї-Якоб Лебель

Ні, також зворотні котирування (backticks) знімають символи нового рядка.
Каміль Маціоровський

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