Как мне выполнить любую команду, редактирующую ее файл (аргумент) «на месте» с помощью bash?

StackOverflow https://stackoverflow.com/questions/146435

  •  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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top