Почему перенаправление вывода файла на себя создает пустой файл?

19

Почему перенаправление вывода файла на себя создает пустой файл?

Заявлено в Bash, почему

less foo.txt > foo.txt

и

fold foo.txt > foo.txt

производить пустой foo.txt? Поскольку при добавлении, например, less eggs.py >> eggs.pyсоздается две копии текста eggs.py, можно ожидать, что при перезаписи будет получена одна копия текста.

Заметьте, я не говорю, что это ошибка, это скорее указатель на что-то глубокое в Unix.

seewalker
источник
Адресовано в каноническом U & L. Каковы операторы управления и перенаправления оболочки? вопрос.
Скотт

Ответы:

20

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

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

Если вы хотите использовать файл как для ввода, так и для вывода с командой, которая не поддерживает модификацию места, вы можете использовать несколько обходных путей:

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

    fold foo.txt > fold.txt.$$ && mv fold.txt.$$ foo.txt
  • Избегайте промежуточного файла за счет возможной частичной или полной потери данных в случае ошибки или прерывания. В этом примере содержимое foo.txtпередается в качестве входных данных в подоболочку (внутри скобок) перед удалением файла. Предыдущий инод остается активным, поскольку подоболочка сохраняет его открытым во время чтения данных. Файл, написанный внутренней утилитой (здесь fold) с тем же именем (foo.txt) указывает на другой индекс, потому что старая запись каталога была технически удалена, во время процесса есть два разных «файла» с одинаковыми именами. Когда подоболочка заканчивается, старый инод освобождается и его данные теряются. Будьте осторожны, чтобы убедиться, что у вас достаточно места для временного хранения старого и нового файлов одновременно, иначе вы потеряете данные.

    (rm foo.txt; fold > foo.txt) < foo.txt
jlliagre
источник
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}". Но вы пропустили еще одну проблему с этим ответом: он говорит «subshell», когда он означает «подстановка команд». Да, подстановки команд обычно являются подоболочками, но не наоборот, и подоболочки, как правило, не помогают в этой проблеме.
Скотт
@KamilMaciorowski Мне так не хватает всего этого. Спасибо за указание всего этого. Для вашего (4)-го пункта: будут ли обратные кавычки добиваться цели, то есть сохранять лидирующие символы новой строки?
Луи-Джейкоб Лебель
@ Скотт спасибо за ваш ответ. Я изменил «subshell» на «подстановка команд». Кстати, мне интересно, какая именно разница между ними.
Луи-Джейкоб Лебель
Нет, обратные кавычки (обратные кавычки) также удаляют завершающие символы новой строки.
Камиль Мачоровский
Хорошо, тогда я добавил предупреждение на данный момент. Я удалю его, если найду решение.
Луи-Джейкоб Лебель