Как выполнить любую команду, редактируя свой файл (аргумент) «на месте», используя bash? - PullRequest
108 голосов
/ 28 сентября 2008

У меня есть файл temp.txt, который я хочу отсортировать с помощью команды sort в bash.

Я хочу, чтобы отсортированные результаты заменили исходный файл.

Это не работает, например (я получаю пустой файл):

sortx temp.txt > temp.txt

Можно ли сделать это в одну строку, не прибегая к копированию во временные файлы?


EDIT: опция -o очень крутая для sort. Я использовал sort в своем вопросе в качестве примера. Я столкнулся с той же проблемой с другими командами:

uniq temp.txt > temp.txt.

Есть ли лучшее общее решение?

Ответы [ 14 ]

171 голосов
/ 28 сентября 2008
sort temp.txt -o temp.txt
29 голосов
/ 29 сентября 2008

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 гласит:

Если выходной файл [] является одним из входных файлов, команда sort копирует его во временный файл, а затем сортирует и записывает выходные данные в выходной файл [].

Такие команды, как uniq, могут начать запись вывода до того, как они закончат чтение ввода. Эти команды обычно не поддерживают редактирование на месте (и им будет сложнее поддерживать эту функцию).

Обычно вы работаете с этим временным файлом, или, если вы абсолютно не хотите иметь промежуточный файл, вы можете использовать буфер для сохранения полного результата перед его записью. Например, с perl:

uniq temp.txt | perl -e 'undef $/; $_ = <>; open(OUT,">temp.txt"); print OUT;'

Здесь часть perl читает полный вывод из uniq в переменной $_, а затем переписывает исходный файл с этими данными. Вы можете сделать то же самое на языке сценариев по вашему выбору, возможно, даже в Bash. Но учтите, что для хранения всего файла потребуется достаточно памяти, это не рекомендуется при работе с большими файлами.

19 голосов
/ 11 декабря 2010

Вот более общий подход, работает с uniq, sort и еще много чего.

{ rm file && uniq > file; } < file
9 голосов
/ 05 июля 2013

Комментарий Тобу о губке гарантирует, что сам по себе является ответом.

Цитировать с 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

Э-э, my-important-file ушел.

6 голосов
/ 28 сентября 2008

Вот, пожалуйста, одна строка:

sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt

Технически нет копирования во временный файл, и команда 'mv' должна быть мгновенной.

4 голосов
/ 12 июля 2013

Мне нравится ответ sort file -o file, но я не хочу вводить одно и то же имя файла дважды.

Использование BASH расширение истории :

$ sort file -o !#^

захватывает первый аргумент текущей строки при нажатии , введите .

Уникальная сортировка на месте:

$ sort -u -o file !#$

захватывает последний аргумент в текущей строке.

3 голосов
/ 17 января 2015

Альтернатива 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
3 голосов
/ 29 сентября 2008

Это сильно ограничивает память, но вы можете использовать awk для хранения промежуточных данных в памяти, а затем записать их обратно.

uniq temp.txt | awk '{line[i++] = $0}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt
3 голосов
/ 29 сентября 2008

Многие упоминали опцию -o . Вот часть справочной страницы.

со страницы руководства:

   -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.
1 голос
/ 17 марта 2010

Чтобы добавить возможность uniq, каковы недостатки:

sort inputfile | uniq | sort -o inputfile
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...