Я только что запустил в bash следующее:
uniq .bash_history > .bash_history
и мой файл истории оказался совершенно пустым.
Думаю, мне нужен способ прочитать весь файл перед записью в него. Как это сделать?
PS: Я, очевидно, думал об использовании временного файла, но ищу более элегантное решение.
Я просто хотел предложить другой ответ, простой и не использующий губку (потому что он часто не включается в облегченные среды).
echo "$(uniq .bash_history)" > .bash_history
должен иметь желаемый результат. Подоболочка запускается до открытия .bash_history для записи. Как объясняется в ответе Фила П., к тому времени, когда .bash_history будет прочитан в исходной команде, он уже будет усечен оператором '>'.
Я рекомендую использовать sponge
из Moreutils. На странице руководства:
DESCRIPTION
sponge reads standard input and writes it out to the specified file. Unlike
a shell redirect, sponge soaks up all its input before opening the output file.
This allows for constructing pipelines that read from and write to the same
file.
Чтобы применить это к вашей проблеме, попробуйте:
uniq .bash_history | sponge .bash_history
Проблема в том, что ваша оболочка настраивает конвейер команд перед запуском команд. Дело не в «вводе и выводе», а в том, что содержимое файла исчезло еще до запуска uniq. Это выглядит примерно так:
>
выходной файл для записи, усекая егоСуществуют различные решения, в том числе редактирование на месте и использование временных файлов, о которых упоминают другие, но главное - понять проблему, что на самом деле идет не так и почему.
Еще один трюк, чтобы сделать это без использования sponge
, это следующая команда:
{ rm .bash_history && uniq > .bash_history; } < .bash_history
Это один из читов, описанных в отличной статье Редактирование файлов «на месте» на backreference.org.
По сути, он открывает файл для чтения, а затем «удаляет» его. Однако на самом деле он не удален: на него указывает дескриптор открытого файла, и пока он остается открытым, файл все еще существует. Затем он создает новый файл с тем же именем и записывает в него уникальные строки.
Недостаток этого решения: если uniq
не работает по какой-то причине, ваша история исчезнет.
использовать губку из Moreutils
uniq .bash_history | sponge .bash_history
В качестве интересного лакомого кусочка sed также использует временный файл (он делает это за вас):
$ strace sed -i 's/foo/bar/g' foo
open("foo", O_RDONLY|O_LARGEFILE) = 3
...
open("./sedPmPv9z", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600) = 4
...
read(3, "foo\n"..., 4096) = 4
write(4, "bar\n"..., 4) = 4
read(3, ""..., 4096) = 0
close(3) = 0
close(4) = 0
rename("./sedPmPv9z", "foo") = 0
close(1) = 0
close(2) = 0
Описание:
Временный файл ./sedPmPv9z
становится fd 4, а foo
files становится fd 3. Операции чтения выполняются на fd 3, а записи - на fd 4 (временный файл). Затем файл foo перезаписывается временным файлом при вызове переименования.
это sed
скрипт удаляет соседние дубликаты. С -i
вариант, он выполняет модификацию на месте. Это из sed
info
файл:
sed -i 'h;:b;$b;N;/^\(.*\)\n\1$/ {g;bb};$b;P;D' .bash_history
Другое решение:
uniq file 1<> líneas.txt
Временный файл - это почти все, если только данная команда не поддерживает редактирование на месте (uniq
нет - некоторые sed
я делаю (sed -i
)).
Вы можете использовать Vim в режиме Ex:
ex -sc '%!uniq' -cx .bash_history
%
выбрать все строки
!
Команда выполнения
x
сохранить и закрыть
Вы также можете использовать tee, используя вывод uniq в качестве ввода:
uniq .bash_history | tee .bash_history