Найти и заменить в файле и перезаписать файл не работает, он очищает файл

604

Я хотел бы запустить поиск и замену файла HTML через командную строку.

Моя команда выглядит примерно так:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html > index.html

Когда я запускаю это и смотрю на файл позже, он пуст. Он удалил содержимое моего файла.

Когда я запускаю это после восстановления файла снова:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

stdoutЭто содержимое файла, а также поиска и замены была выполнена.

Почему это происходит?

BBales
источник
13
Альтернатива Perl:perl -pi -w -e 's/STRING_TO_REPLACE/REPLACE_WITH/g;' index.html
Gjorgji Tashkovski
много связанных sedкоманд, чтобы найти строку и заменить всю строку: stackoverflow.com/questions/11245144/…
cregox

Ответы:

917

Когда оболочка видит > index.htmlв командной строке, она открывает файл index.htmlдля записи , стирая все его предыдущее содержимое.

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

sed -i.bak s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

Без .bak команда не будет работать на некоторых платформах, таких как Mac OSX.

codaddict
источник
20
Сказать, truncates the fileа не, opens the fileвероятно, делает это яснее.
Микель
12
По крайней мере, на моем Mac первое предложение не работает ... если вы делаете замену файла на месте, вы должны указать расширение. Вы можете, по крайней мере, передать расширение нулевой длины: sed -i 's / STRING_TO_REPLACE / STRING_TO_REPLACE_IT / g index.html
Том Лианца
5
для переменных sed -i.bak 's /' $ search '/' $ replace '/ g' index.html
Фатима Зора
33
на OSX используйте пустую строку '' в качестве параметра для -i, например:sed -i '' 's/blah/xx/g'
Pierre Houston
4
а что твое .bakпосле sed -i?
Патрицио Бертони
210

Альтернативный, полезный шаблон:

sed -e 'script script' index.html > index.html.tmp && mv index.html.tmp index.html

Это имеет почти тот же эффект, без использования этой -iопции, и дополнительно означает, что, если сценарий sed завершается ошибкой по какой-то причине, входной файл не засоряется. Кроме того, если редактирование прошло успешно, резервной копии не осталось. Этот вид идиомы может быть полезен в Makefiles.

Довольно много у seds есть -iвыбор, но не все из них; posix sed - это тот, который не делает. Поэтому, если вы стремитесь к мобильности, этого лучше избегать.

Норман грей
источник
9
+1 за отсутствие резервной копии файла и не засорение входного файла, если редактирование не удалось. Работал безупречно на Mac.
Майк Грейс,
У меня сработало отлично. Спасибо! (на Mac)
интересно
1
Это прекрасно сработало, когда на Ubuntu Server 14.04 sed -i постоянно обнулял файл.
Крис Гиддингс
2
Чрезвычайно незначительное улучшение:... && mv index.html{.tmp,}
Эдвард Гарсон
5
@EdwardGarson Действительно, это, вероятно, то, что я бы использовал, если бы набрал его - я согласен, что он аккуратнее - но sh(если я правильно помню) этого {...}расширения нет. В Makefile вы можете использовать, shа не bash, поэтому, если вы стремитесь к переносимости (или posixness), то вам нужно избегать этой конструкции.
Норман Грей,
95
sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' index.html

Это делает глобальную замену на месте в файле index.html. Кавычка строки предотвращает проблемы с пробелами в запросе и замене.

Богатая Аподака
источник
57

используйте опцию sed -i, например

sed -i bak -e s/STRING_TO_REPLACE/REPLACE_WITH/g index.html
Kevin
источник
Что это значит? sed: -i нельзя использовать с stdin
sheetal
2
Не забудьте заключить ваш шаблон в кавычки, если он содержит пробелы -'s/STRING_TO_REPLACE/REPLACE_WITH/g'
Дуг Томпсон,
@sheetal: -iвыполняет редактирование файлов на месте , поэтому нет смысла комбинировать его с вводом stdin .
mklement0
Это может работать на macOS, но не на Arch Linux для меня.
xdevs23
Без -е принятый ответ не работает на MacOS, Каталина. С -е это работает.
cwhiii
18

Чтобы изменить несколько файлов (и сохранить резервную копию каждого как * .bak):

perl -p -i -e "s/\|/x/g" *  

будет принимать все файлы в каталоге и заменить |с x этим называется «Perl пирог» (легко , как пирог)

Stenemo
источник
1
Приятно видеть кого-то, кто хочет взглянуть на постановку задачи, а не только на теги. OP не указывал sedв качестве требования, он использовался только в качестве уже опробованного инструмента.
user7412956
14

Вы должны попробовать использовать опцию -iдля редактирования на месте.

uloBasEI
источник
6
sed -i.bak "s#https.*\.com#$pub_url#g" MyHTMLFile.html

Если у вас есть ссылка для добавления, попробуйте это. Найдите URL-адрес, как указано выше (начиная с https и заканчивая здесь ..com), и замените его строкой URL-адреса. Я использовал переменную $pub_urlздесь. sздесь означает поиск и gозначает глобальную замену.

Оно работает !

Kaey
источник
6

Предупреждение: это опасный метод! Он использует буферы ввода / вывода в linux и с определенными параметрами буферизации ему удается работать с небольшими файлами. Это интересное любопытство. Но не используйте это для реальной ситуации!

Кроме -iтого, sed вы можете использовать teeутилиту .

От man:

tee - чтение из стандартного ввода и запись в стандартный вывод и файлы

Итак, решение будет таким:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee | tee index.html

- здесь teeповторяется, чтобы убедиться, что конвейер буферизован. Затем все команды в конвейере блокируются, пока они не получат некоторый ввод для работы. Каждая команда в конвейере запускается, когда вышестоящие команды записывают 1 буфер байтов (размер где-то определен ) на вход команды. Таким образом, последняя команда tee index.html, которая открывает файл для записи и, следовательно, очищает его, выполняется после завершения восходящего конвейера и вывода в буфер внутри конвейера.

Скорее всего, следующее не будет работать:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee index.html

- он будет запускать обе команды конвейера одновременно без какой-либо блокировки. (Без блокировки трубопровода должны пройти байты построчно вместо буфера буфера. То же, что при запуске cat | sed s/bar/GGG/. Без блокировки это более интерактивные и , как правило трубопроводы всего 2 команд работают без буферизации и блокировки. Более длинные трубопроводы буферные.) В tee index.htmlволе откройте файл для записи, и он будет очищен. Однако, если вы включите буферизацию всегда, вторая версия тоже будет работать.

xealits
источник
3
Выходной файл tee также открывается немедленно, что приводит к пустому index.html для всей команды.
sjngm
3
Это приведет к повреждению любого входного файла, который больше буфера конвейера (который обычно составляет 64 КБ) . (@sjngm: файл не усекается сразу же, как при использовании >, но точка зрения состоит в том, что это неверное решение, которое может привести к потере данных).
mklement0
4

Проблема с командой

sed 'code' file > file

в том file, что оболочка усекается до того, как sed действительно его обработает. В результате вы получите пустой файл.

SED способ сделать это, чтобы использовать -iдля редактирования на месте, как предложили другие ответы. Однако это не всегда то, что вы хотите. -iсоздаст временный файл, который затем будет использован для замены исходного файла. Это проблематично, если в вашем исходном файле была ссылка (ссылка будет заменена обычным файлом). Если вам нужно сохранить ссылки, вы можете использовать временную переменную для хранения вывода sed перед записью его обратно в файл, например так:

tmp=$(sed 'code' file); echo -n "$tmp" > file

Еще лучше использовать printfвместо, echoтак echoкак, вероятно, будет обрабатываться \\как \в некоторых оболочках (например, тире):

tmp=$(sed 'code' file); printf "%s" "$tmp" > file
Анджей Пронобис
источник
1
+1 за сохранение ссылок. Он также работает с временным файлом:sed 'code' file > file.tmp; cat file.tmp > file; rm file.tmp
dashohoxha
3

И edответ:

printf "%s\n" '1,$s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' w q | ed index.html

Чтобы повторить ответ codaddict , оболочка сначала обрабатывает перенаправление , стирая файл «input.html», а затем оболочка вызывает команду «sed», передавая ей теперь пустой файл.

Гленн Джекман
источник
2
Быстрый вопрос, почему люди продолжают давать « edверсию» sedответов? это работает быстрее?
Cregox
6
Некоторые sedне реализуют -iдля редактирования на месте. edвездесущ и позволяет вам сохранить ваши изменения в исходном файле. Плюс всегда хорошо иметь много инструментов в своем наборе.
Гленн Джекман
Окей круто. Итак, с точки зрения производительности, я думаю, они одинаковы. Спасибо!
cregox
2

Вы можете использовать Vim в режиме Ex:

ex -sc '%s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g|x' index.html
  1. % выбрать все строки

  2. x сохранить и закрыть

Стивен Пенни
источник
0

Я искал вариант, где я могу определить диапазон строк и нашел ответ. Например, я хочу изменить host1 на host2 из строки 36-57.

sed '36,57 s/host1/host2/g' myfile.txt > myfile1.txt

Вы также можете использовать опцию gi, чтобы игнорировать регистр символов.

sed '30,40 s/version/story/gi' myfile.txt > myfile1.txt

источник
0

При всем уважении к правильным ответам, приведенным выше, всегда полезно запускать такие сценарии «пробного запуска», чтобы вы не повредили файл и не начали заново.

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

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

ИЛИ

less index.html | sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g 

Таким образом, вы можете увидеть и проверить вывод команды без усечения файла.

Нестор Миляев
источник