Я пытаюсь выяснить, возможно ли отредактировать файл в одной команде sed без вручную потокового редактирования отредактированного содержимого в новый файл, а затем переименовать новый файл в исходное имя файла, Я попробовал параметр -i
, но моя система Solaris сообщила, что -i
является незаконным вариантом. Есть ли другой способ?
Sed редактировать файл на месте
Ответ 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 /' {} \;