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

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

Ответ 1

Опция -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

на macOS.

Ответ 2

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

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

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

Ответ 3

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

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

Это связано с тем, что в версии OSX sed параметр -i ожидает аргумент extension, поэтому ваша команда фактически анализируется как аргумент extension, а путь к файлу интерпретируется как код команды. Источник: fooobar.com/questions/33995/...

Ответ 4

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

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

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

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

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

Ответ 5

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

Метод тройника

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

Метод исправления

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

UPDATE:

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

Патч не должен знать, к какому файлу обращаться, поскольку он найден в первой строке вывода из 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

Ответ 6

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

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

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

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

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

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

Ответ 7

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 со старым контентом.

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

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

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

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

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

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

Ответ 11

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

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

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

Ответ 12

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

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

Приятного редактирования на месте. Добавлен \nw\n для записи файла. Извиняюсь за задержку ответа на запрос.

Ответ 13

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

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