Знайти та замінити у файлі та перезаписати файл не працює, він видаляє файл


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

The stdout вміст файлу, а пошук і заміна виконано.

Чому це відбувається?


13
Альтернатива Perl:perl -pi -w -e 's/STRING_TO_REPLACE/REPLACE_WITH/g;' index.html
Джорджи Ташковський

багато пов'язаних 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.


20
Сказання truncates the fileзамість, opens the fileмабуть, робить це зрозумілішим.
Мікель

12
Принаймні, на моєму комп'ютері, перша пропозиція не працює ... якщо ви замінюєте файл на місці, вам потрібно вказати розширення. Ви можете, принаймні, пропустити нульову довжину, хоча: sed -i 's / STRING_TO_REPLACE / STRING_TO_REPLACE_IT / g index.html
Том Ліанса

5
для змінних sed -i.bak 's /' $ search '/' $ zame '/ g' index.html
Фатіма Зохра

33
на osx, використовуйте порожній рядок '' як параметр для -i, наприклад:sed -i '' 's/blah/xx/g'
П'єр Х'юстон,

4
але що твоє .bakпісля sed -i?
Патрісіо Бертоні

210

Альтернативою, корисною схемою є:

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

Це має такий же ефект, не використовуючи -iпараметр, і додатково означає, що, якщо скрипт sed з якоїсь причини виходить з ладу, вхідний файл не клобується. Крім того, якщо редагування вдалося, не залишається жодного резервного файла. Цей вид ідіоми може бути корисним у Makefiles.

Досить багато наборів є -iможливість, але не всі з них; posix sed - це той, який ні. Якщо ви прагнете переносимості, то краще всього цього уникати.


9
+1, якщо файл резервного копіювання не прокладається навколо, а не вводить вхідний файл, якщо редагування не вдалося Працювали бездоганно на mac.
Майк Грейс

Працювали для мене чудово. Дякую! (на Mac)
зацікавився

1
Це чудово працювало для мене, коли на Ubuntu Server 14.04 sed -i продовжував нульовий файл.
Кріс Гіддінгс

2
Надзвичайно незначне покращення:... && mv index.html{.tmp,}
EdwardGarson

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 -і, наприклад

sed -i bak -e s/STRING_TO_REPLACE/REPLACE_WITH/g index.html

Що це означає? 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 пиріг» (легко , як пиріг)


1
Приємно бачити когось, хто бажає подивитися на постанову проблеми, а не лише теги. OP не вказав sedяк вимогу, використовував її лише як спробуваний інструмент.
користувач7412956

14

Спробуйте скористатися опцією -iдля редагування на місці.


6
sed -i.bak "s#https.*\.com#$pub_url#g" MyHTMLFile.html

Якщо у вас є посилання, яке потрібно додати, спробуйте це. Шукайте URL-адресу, як описано вище (починаючи з https та закінчуючи з.com.ua тут) та замініть її рядком URL-адреси. Я тут використав змінну $pub_url. sтут означає пошук іg означає глобальну заміну.

Це працює !


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волі відкрийте файл для запису, і він буде очищений. Однак якщо буферизацію завжди включати, друга версія теж буде працювати.


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

Щоб повторити, на що відповів кодекс , оболонка спочатку обробляє перенаправлення , витираючи файл "input.html", а потім оболонка викликає команду "sed", передаючи їй тепер порожній файл.


2
швидке запитання, чому люди продовжують давати " edверсію" sedвідповідей? це виконує швидше?
Крегокс

6
Деякі sedз них не здійснюють -iредагування на місці. edє всюдисущим і дозволяє вам зберігати свої зміни в оригінальному файлі. Крім того, завжди добре мати багато інструментів у своєму комплекті.
glenn jackman

ОК здорово. тож, на розумну ефективність, я думаю, вони такі самі. Дякую!
Крегокс

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 

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.