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 /' {} \;