Как мне выполнить любую команду, редактирующую ее файл (аргумент) «на месте» с помощью bash?
-
02-07-2019 - |
Вопрос
У меня есть файл temp.txt, который я хочу отсортировать с помощью sort
команда в bash.
Я хочу, чтобы отсортированные результаты заменили исходный файл.
Например, это не работает (я получаю пустой файл):
sortx temp.txt > temp.txt
Можно ли это сделать одной строкой, не прибегая к копированию во временные файлы?
РЕДАКТИРОВАТЬ:А -o
вариант очень крутой sort
.я использовал sort
в моем вопросе в качестве примера.Я столкнулся с той же проблемой с другими командами:
uniq temp.txt > temp.txt.
Есть ли лучшее общее решение?
Решение
sort temp.txt -o temp.txt
Другие советы
А sort
необходимо просмотреть весь ввод, прежде чем он сможет начать вывод.По этой причине sort
программа может легко предложить возможность изменить файл на месте:
sort temp.txt -o temp.txt
В частности, документация GNU sort
говорит:
Обычно функция sort считывает все входные данные перед открытием выходного файла, поэтому вы можете безопасно сортировать файл на месте, используя такие команды, как
sort -o F F
иcat F | sort -o F
.Однако,sort
с--merge
(-m
) может открыть выходной файл перед чтением всех входных данных, поэтому команда типаcat F | sort -m -o F - G
небезопасно, так как сортировка может начать писатьF
доcat
закончил читать.
Хотя документация BSD sort
говорит:
Если [выходной файл] является одним из входных файлов, sort копирует его во временный файл перед сортировкой и записью вывода в [выходной файл].
Такие команды, как uniq
могут начать записывать выходные данные до того, как они закончат чтение входных данных.Эти команды обычно не поддерживают редактирование на месте (и им было бы сложнее поддерживать эту функцию).
Обычно вы решаете эту проблему с помощью временного файла, или, если вы абсолютно хотите избежать использования промежуточного файла, вы можете использовать буфер для хранения полного результата перед его записью.Например, с perl
:
uniq temp.txt | perl -e 'undef $/; $_ = <>; open(OUT,">temp.txt"); print OUT;'
Здесь часть Perl считывает полный вывод из uniq
в переменной $_
а затем перезаписывает исходный файл этими данными.Вы можете сделать то же самое на любом языке сценариев, возможно, даже в Bash.Но учтите, что для хранения всего файла потребуется достаточно памяти, при работе с большими файлами это нецелесообразно.
Вот более общий подход, работающий с uniq, sort и еще много чего.
{ rm file && uniq > file; } < file
Комментарий Тобу о губке гарантирует, что это ответ сам по себе.
Цитирую из дополнительные утилиты домашняя страница:
Вероятно, на данный момент самым универсальным инструментом в moreutils является губка(1), которая позволяет делать такие вещи:
% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd
Однако, sponge
страдает от той же проблемы Стив Джессоп комментирует здесь. Если какая-либо из команд в конвейере перед sponge
потерпит неудачу, то исходный файл будет перезаписан.
$ mistyped_command my-important-file | sponge my-important-file
mistyped-command: command not found
Ой-ой, my-important-file
ушел.
Вот, одна строчка:
sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt
Технически копирование во временный файл невозможно, и команда «mv» должна выполняться мгновенно.
мне нравится sort file -o file
ответ, но не хочу дважды вводить одно и то же имя файла.
Использование БАШ расширение истории:
$ sort file -o !#^
захватывает первый аргумент текущей строки при нажатии входить.
Уникальная сортировка на месте:
$ sort -u -o file !#$
захватывает последний аргумент в текущей строке.
Многие упомянули -о вариант.Вот часть man-страницы.
Со страницы руководства:
-o output-file
Write output to output-file instead of to the standard output.
If output-file is one of the input files, sort copies it to a
temporary file before sorting and writing the output to output-
file.
Это будет сильно ограничивать память, но вы можете использовать awk для хранения промежуточных данных в памяти, а затем записать их обратно.
uniq temp.txt | awk '{line[i++] = $0}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt
Альтернатива sponge
с более распространенным sed
:
sed -ni r<(command file) file
Это работает для любой команды (sort
, uniq
, tac
, ...) и использует очень известный sed
's -i
вариант (редактировать файлы на месте).
Предупреждение: Пытаться command file
во-первых, потому что редактирование файлов на месте небезопасно по своей природе.
Объяснение
Во-первых, вы говорите sed
не печатать (исходные) строки (-n
вариант), и с помощью sed
's r
команда и bash
's Замена процесса, сгенерированный контент <(command file)
будет сохранен вывод на месте.
Делаем вещи еще проще
Вы можете обернуть это решение в функцию:
ip_cmd() { # in place command
CMD=${1:?You must specify a command}
FILE=${2:?You must specify a file}
sed -ni r<("$CMD" "$FILE") "$FILE"
}
Пример
$ cat file
d
b
c
b
a
$ ip_cmd sort file
$ cat file
a
b
b
c
d
$ ip_cmd uniq file
$ cat file
a
b
c
d
$ ip_cmd tac file
$ cat file
d
c
b
a
$ ip_cmd
bash: 1: You must specify a command
$ ip_cmd uniq
bash: 2: You must specify a file
Используйте аргумент --output=
или -o
Только что попробовал на FreeBSD:
sort temp.txt -otemp.txt
Чтобы добавить uniq
возможности, какие недостатки:
sort inputfile | uniq | sort -o inputfile
Прочтите о неинтерактивном редакторе, ex
.
Если вы настаиваете на использовании sort
программе, вам придется использовать промежуточный файл - я не думаю, что sort
имеет возможность сортировки в памяти.Любой другой трюк с stdin/stdout потерпит неудачу, если вы не сможете гарантировать, что размер буфера для stdin сортировки достаточно велик, чтобы вместить весь файл.
Редактировать:Мне стыдно. sort temp.txt -o temp.txt
работает отлично.
Другое решение:
uniq file 1<> file