Ответ 1
sort temp.txt -o temp.txt
У меня есть файл temp.txt, который я хочу сортировать с помощью команды sort
в bash.
Я хочу, чтобы отсортированные результаты заменили исходный файл.
Это не работает, например (я получаю пустой файл):
sortx temp.txt > temp.txt
Можно ли это сделать в одной строке, не прибегая к копированию во временные файлы?
EDIT: параметр -o
очень крут для sort
. Я использовал sort
в моем вопросе в качестве примера. Я сталкиваюсь с той же проблемой с другими командами:
uniq temp.txt > temp.txt.
Есть ли лучшее общее решение?
sort temp.txt -o temp.txt
A sort
должен видеть все входные данные, прежде чем он сможет начать вывод. По этой причине программа sort
может легко предоставить возможность изменить файл на месте:
sort temp.txt -o temp.txt
В частности, в документации документации GNU sort
говорится:
Обычно сортировка считывает все входные данные перед открытием выходного файла, поэтому вы можете безопасно сортировать файл на месте с помощью команд типа
sort -o F F
иcat F | sort -o F
. Тем не менее,sort
с--merge
(-m
) может открыть выходной файл перед чтением всего ввода, поэтому команда типаcat F | sort -m -o F - G
небезопасна, так как сортировка может начать записьF
, прежде чемcat
будет прочитано.
Пока в документации BSD sort
говорится:
Если [the] output-file является одним из входных файлов, сортируйте его во временный файл перед сортировкой и записью вывода в выходной файл.
Команды, такие как uniq
, могут начать запись вывода до завершения чтения ввода. Эти команды обычно не поддерживают редактирование на месте (и им будет сложнее поддерживать эту функцию).
Обычно вы работаете с временным файлом или если вы абсолютно не хотите иметь промежуточный файл, вы можете использовать буфер для хранения полного результата перед его записью. Например, при perl
:
uniq temp.txt | perl -e 'undef $/; $_ = <>; open(OUT,">temp.txt"); print OUT;'
Здесь часть perl считывает полный вывод из uniq
в переменной $_
, а затем перезаписывает исходный файл этими данными. Вы можете сделать то же самое на языке сценариев по вашему выбору, возможно, даже в Bash. Но учтите, что для хранения всего файла потребуется достаточно памяти, это не рекомендуется при работе с большими файлами.
Здесь более общий подход, работает с uniq, sort и whatnot.
{ rm file && uniq > file; } < file
Комментарий Тобу о губке гарантирует, что это ответ сам по себе.
Чтобы процитировать страницу moreutils:
Наверное, наиболее универсальным инструментом в moreutils пока является sponge (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
Uh-oh, my-important-file
отсутствует.
Здесь вы идете, одна строка:
sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt
Технически нет копирования во временный файл, а команда "mv" должна быть мгновенной.
Мне нравится ответ sort file -o file
, но вы не хотите вводить одно и то же имя файла дважды.
Использование BASH расширение истории:
$ sort file -o !#^
захватывает текущую строку сначала arg, когда вы нажимаете enter.
Уникальный вид на месте:
$ sort -u -o file !#$
захватывает последний аргумент в текущей строке.
Многие упомянули опцию -o. Вот часть страницы man.
На странице 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.
Альтернатива sponge
с более распространенным sed
:
sed -ni r<(command file) file
Он работает для любой команды (sort
, uniq
, tac
,...) и использует очень хорошо известную sed
-i
(редактировать файлы на месте).
Предупреждение: Попробуйте command file
сначала, потому что редактирование файлов на месте не безопасно по своей природе.
Во-первых, вы сообщаете sed
не печатать (оригинальные) строки (-n
вариант), а с помощью команды sed
r
и bash
Замена процесса, сгенерированный контент <(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
Это будет сильно ограничено памятью, но вы можете использовать awk для хранения промежуточных данных в памяти, а затем записать его обратно.
uniq temp.txt | awk '{line[i++] = $0}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt
Прочитайте неинтерактивный редактор ex
.
Используйте аргумент --output=
или -o
Просто попробовал FreeBSD:
sort temp.txt -otemp.txt
Чтобы добавить возможность uniq
, каковы нижние грани для:
sort inputfile | uniq | sort -o inputfile
Если вы настаиваете на использовании программы sort
, вы должны использовать промежуточный файл - я не думаю, что sort
имеет возможность сортировки в памяти. Любой другой трюк с stdin/stdout не удастся, если вы не можете гарантировать, что размер буфера для сортировки stdin достаточно велик, чтобы соответствовать всему файлу.
Править: стыдно за меня. sort temp.txt -o temp.txt
отлично работает.
Другое решение:
uniq file 1<> file