sed редактировать файл на месте

Я пытаюсь выяснить, можно ли редактировать файл в одной команде sed без вручную потоковая передача отредактированного содержимого в новый файл, а затем переименование нового файла в исходное имя файла. Я попробовал -i вариант, но моя система Solaris сказала, что -i является незаконным вариантом. Есть ли другой способ?

13 ответов


на -i опции потоки отредактированного контента в новый файл, а затем переименовывает его за кулисами, в любом случае.

пример:

sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename

и

sed -i '' 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename

on macOS.


в системе, где sed не имеет возможности редактировать файлы на месте, я думаю, что лучшим решением будет использовать perl:

perl -pi -e 's/foo/bar/g' file.txt

хотя это создает временный файл, он заменяет оригинал, потому что был предоставлен пустой суффикс/расширение на месте.


обратите внимание, что в OS X вы можете получить странные ошибки, такие как "недопустимый код команды" или другие странные ошибки при выполнении этой команды. Чтобы устранить эту проблему, попробуйте

sed -i '' -e "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g" <file>

это потому, что на OSX версии sed на ждет extension аргумент, поэтому ваша команда фактически анализируется как extension аргумент и путь к файлу интерпретируются как код команды. Источник: https://stackoverflow.com/a/19457213


следующее отлично работает на моем mac

sed -i.bak 's/foo/bar/g' sample

мы заменяем foo на bar в файле образца. Резервная копия исходного файла будет сохранена в sample.бак!--3-->

для редактирования inline без резервного копирования используйте следующую команду

sed -i'' 's/foo/bar/g' sample

одно следует отметить,sed не может писать файлы самостоятельно, поскольку единственная цель sed-выступать в качестве редактора в "потоке" (т. е. конвейерах stdin, stdout, stderr и других >&n буферов, розетки и тому подобное). Имея это в виду, вы можете использовать другую команду tee для записи результатов в файл. Другой вариант-создать патч из конвейера содержимого в diff.

метод тройник

sed '/regex/' <file> | tee <file>

патч метод

sed '/regex/' <file> | diff -p <file> /dev/stdin | patch

обновление:

кроме того, обратите внимание, что патч получит файл для изменения из строки 1 вывода diff:

Patch не нужно знать, какой файл для доступа, так как это находится в первой строке вывода из diff:

$ echo foobar | tee fubar

$ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin
*** fubar   2014-03-15 18:06:09.000000000 -0500
--- /dev/stdin  2014-03-15 18:06:41.000000000 -0500
***************
*** 1 ****
! foobar
--- 1 ----
! fubar

$ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin | patch
patching file fubar

невозможно сделать то, что вы хотите с sed. Даже версии sed в поддержку -i опция для редактирования файла на месте сделать именно то, что вы явно заявили, что вы не хотите: они пишут во временный файл, а затем переименовать файл. Но, возможно, вы можете просто использовать ed. Например, чтобы изменить все вхождения foo to bar в файле file.txt, вы можете сделать:

echo ',s/foo/bar/g; w' | tr \; '2' | ed -s file.txt

синтаксис похож на sed, но конечно не точно тот же.

но, возможно, вам следует подумать, почему вы не хотите использовать переименованный временный файл. Даже если у вас нет -i поддержка sed, вы можете легко написать сценарий, чтобы сделать работу за вас. Вместо sed -i 's/foo/bar/g' file, вы могли бы сделать inline file sed 's/foo/bar/g'. Такой сценарий тривиален для написания. Например:

#!/bin/sh -e
IN=
shift
trap 'rm -f $tmp' 0
tmp=$( mktemp )
<$IN "$@" >$tmp && cat $tmp > $IN  # preserve hard links

должно быть достаточно для большинства применений.


sed поддерживает редактирование на месте. От man sed:

-i[SUFFIX], --in-place[=SUFFIX]

    edit files in place (makes backup if extension supplied)

пример:

Допустим у вас есть файл hello.txtС текстом:
hello world!

если вы хотите сохранить резервную копию старого файла, использовать:

sed -i.bak 's/hello/bonjour' hello.txt

вы получите два файла:hello.txt содержание:

bonjour world!

и hello.txt.bak со старым содержимым.

если вы не хотите, чтобы сохранить копию, просто не передать параметр расширения.


вы можете использовать vi

vi -c '%s/foo/bar/g' my.txt -c 'wq'

mv file.txt file.tmp && sed 's/foo/bar/g' < file.tmp > file.txt

следует сохранить все жесткие ссылки, так как выходные данные направляются обратно для перезаписи содержимого исходного файла и избегают необходимости в специальной версии sed.


если вы заменяете такое же количество символов и после тщательного чтения "на месте" редактирование файлов...

вы также можете использовать оператор перенаправления <> открыть файл для чтения и записи:

sed 's/foo/bar/g' file 1<> file

смотрите в прямом эфире:

$ cat file
hello
i am here                           # see "here"
$ sed 's/here/away/' file 1<> file  # Run the `sed` command
$ cat file
hello
i am away                           # this line is changed now

С справочное руководство Bash → 3.6.10 Открытие дескрипторов файлов для чтения и записи:

оператор перенаправления

[n]<>word

вызывает файл, имя которого является расширением word, который будет открыт для чтение и запись в файловом дескрипторе n или в файловом дескрипторе 0 если N не указано. Если файл не существует, он создается.


вы не указали, какую оболочку вы используете, но с zsh вы можете использовать =( ) строительство, чтобы добиться этого. Что-то вроде:

cp =(sed ... file; sync) file

=( ) похож на >( ) но создает временный файл, который автоматически удаляется при cp завершается.


Как сказал Манипенни в Skyfall: "иногда старые способы лучше." Позже Кинкейд сказал нечто подобное.

$ printf ', s/false/true | g\nw\n' / ed {YourFileHere}

счастливое редактирование на месте. Добавлена '\nw\n ' для записи файла. Прошу прощения за задержку с ответом на запрос.


очень хорошие примеры. У меня была проблема с редактированием многих файлов, и опция-i кажется единственным разумным решением, использующим ее в команде find. Здесь скрипт для добавления "version:" перед первой строкой каждого файла:

find . -name pkg.json -print -exec sed -i '.bak' '1 s/^/version /' {} \;