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

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

15 ответов

Решение

-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.

В системе, где 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, -i вариант ожидает extension аргумент, так что ваша команда на самом деле анализируется как extension аргумент и путь к файлу интерпретируется как код команды. Источник: /questions/47801959/nedopustimyij-kod-komandyi-nesmotrya-na-ekranirovanie-ispolzuya-sed/47801987#47801987

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

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

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

Для редактирования 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

ОБНОВИТЬ:

Также обратите внимание, что 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 в bar в файле file.txt, ты можешь сделать:

echo ',s/foo/bar/g; w' | tr \; '\012' | 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=$1
shift
trap 'rm -f $tmp' 0
tmp=$( mktemp )
<$IN "$@" >$tmp && cat $tmp > $IN  # preserve hard links

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

Вы могли бы использовать vi

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

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

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

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

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

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 не указано. Если файл не существует, он создается.

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

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

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

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

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

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

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

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

Чтобы решить эту проблему на Mac, мне пришлось добавить некоторые функции Unix в core-utils после этого файла .

      brew install grep
==> Caveats
All commands have been installed with the prefix "g".
If you need to use these commands with their normal names, you
can add a "gnubin" directory to your PATH from your bashrc like:
  PATH="/usr/local/opt/grep/libexec/gnubin:$PATH"

Позвонить с gsedвместо sed. Mac по умолчанию не нравится, как grep -rlотображает имена файлов с ./предварительно подготовленный.

      ~/my-dir/configs$ grep -rl Promise . | xargs sed -i 's/Promise/Bluebirg/g'

sed: 1: "./test_config.js": invalid command code .

я также должен был использовать xargs -I{} sed -i 's/Promise/Bluebirg/g' {}для файлов с пробелом в имени.

Если вы хотите заменить строки, содержащие '/', вы можете использовать '?'. т.е. замените '/usr/local/bin/python' на '/usr/bin/python3' для всех файлов *.py.

найти . -name \*.py -exec sed -i 's?/usr/local/bin/python?/usr/bin/python3?g' {} \;

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

find . -name pkg.json -print -exec sed -i '.bak' '1 s/^/version /' {} \;
Другие вопросы по тегам