sed редагувати файл на місці


298

Я намагаюся з’ясувати, чи можна редагувати файл в одній команді sed, не вручну відтворюючи відредагований вміст у новий файл, а потім перейменуючи новий файл на вихідний ім'я файлу. Я спробував -iваріант, але моя система Solaris сказала, що -iце незаконний варіант. Чи є інший спосіб?


7
-iє варіантом у gnu sed, але не є у стандартній sed. Однак він передає вміст у новий файл, а потім перейменовує файл, щоб він не був тим, що потрібно.
Вільям Перселл

2
насправді, це те, що я хочу, я просто хочу, щоб я не зазнавав необхідності виконувати загальносвітове завдання перейменування нового файлу на оригінальне ім'я
амфібій

3
Тоді вам потрібно перезаписати питання.
Вільям Перселл

3
@amphibient: Чи не заперечуєте ви проти префіксації заголовка питання словом "Solaris"? Значення вашого запитання втрачається. Будь ласка, дивіться коментарі нижче моєї відповіді. Дякую.
Стів

1
@Steve: Я знову видалив префікс Solaris із заголовка, оскільки це аж ніяк не виключно для Solaris.
трійка

Відповіді:


475

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


3
принаймні, це робить це для мене, тому я не повинен
амфібій

2
Ви можете спробувати скласти GNU sed у вашій системі.
choroba

3
приклад:sed -i "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g" <file>
Фалес Сеолін

2
Це краще, ніж розчин perl. Якось він не створює жодного .swap-файлу .... дякую!
математика

3
@maths: Це так, але, можливо, десь ще чи на коротший час?
choroba

72

У системі, де sedнемає можливості редагувати файли на місці, я думаю, що кращим рішенням було б користуватися perl:

perl -pi -e 's/foo/bar/g' file.txt

Хоча це створює тимчасовий файл, він замінює оригінал, оскільки надано порожній на місці суфікс / розширення.


14
Він не тільки створює тимчасовий файл, але й розриває жорсткі посилання (наприклад, замість того, щоб змінювати вміст файлу, він замінює файл новим тим же ім’ям.) Іноді це потрібна поведінка, іноді це прийнятно, але коли є кілька посилань на файл, це майже завжди неправильна поведінка.
Вільям Перселл

3
@DwightSpencer: Я вважаю, що всі системи без Perl зламані. Одна команда перетворює ланцюжок команд, коли потрібні підвищені дозволи та повинні використовувати їх sudo. На мій погляд, питання полягає в тому, як уникнути створення вручну файлів і перейменування тимчасового файлу без встановлення останнього GNU або BSD sed. Просто використовуйте Perl.
Стів

4
@PetrPeller: Дійсно? Якщо ви уважно прочитали питання, то зрозуміли б, що ОП намагається уникнути чогось подібного sed 's/foo/bar/g' file.txt > file.tmp && mv file.tmp file.txt,. Тільки тому, що на місці редагування відбувається перейменування за допомогою тимчасового файлу, це не означає, що він / вона повинен виконувати перейменування вручну, коли параметр недоступний. Є інші інструменти, які можуть робити те, що він / вона хоче, і Перл - очевидний вибір. Як це не відповідь на початкове запитання?
Стів

2
@a_arias: Ти маєш рацію; він зробив. Але він не може досягти того, що хоче, використовуючи Solaris sed. Питання було насправді дуже хорошим, але його значення було зменшено людьми, які або не вміють читати сторінки людини, або не можуть турбуватися насправді читати запитання тут на ТАК.
Стів

4
@EdwardGarson: IIRC, Solaris постачається з Perl, але не з Python або Ruby. І як я вже говорив раніше, ОП не може досягти бажаного результату за допомогою Solaris sed.
Стів

56

Зауважте, що в OS X ви можете отримати дивні помилки, такі як "недійсний код команди" або інші дивні помилки під час виконання цієї команди. Щоб вирішити цю проблему, спробуйте

sed -i '' -e "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g" <file>

Це тому, що в версії OSX sed, -iопція очікує extensionаргумент, тому ваша команда насправді розбирається як extensionаргумент, а шлях до файлу інтерпретується як код команди. Джерело: https://stackoverflow.com/a/19457213


1
Це ще одна дивовижність ОС X. На щастя, brew install gnu-sedпоряд із PATHзакликом ви зможете використовувати GNU версію sed. Дякую за згадування; певна дряпалка голови.
Дуг

23

Далі добре працює на моєму комп'ютері

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

Ми замінюємо foo на смугу у файлі зразка. Резервне копіювання вихідного файлу буде збережено у sample.bak

Для редагування вбудованого файлу без резервного копіювання використовуйте наступну команду

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

Як запобігти збереженню резервних файлів?
Петр Пеллер

@PetrPeller, Ви можете використовувати згадану команду для того ж ... sed -i '' s / foo / bar / g 'sample
minhas23

18

Слід зазначити, що sedне можна самостійно писати файли, оскільки єдиною метою sed є виступати редактором у "потоці" (тобто трубопроводах stdin, stdout, stderr та інших >&nбуферах, сокетах тощо). Зважаючи на це, ви можете скористатися іншою командою, teeщоб записати результат у файл. Інший варіант - створити патч із прошивки вмісту diff.

Трійковий метод

sed '/regex/' <file> | tee <file>

Патч-метод

sed '/regex/' <file> | diff -p <file> /dev/stdin | patch

ОНОВЛЕННЯ:

Крім того, зауважте, що виправлення отримає файл для зміни з рядка 1 різного виводу:

Патч не потрібно знати, до якого файлу отримати доступ, оскільки це знаходиться в першому рядку виводу з 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

2
/dev/stdin 2014-03-15, відповів 7 січня - ти подорож у часі?
Адріан Фрюхвірт

У Windows, що використовує msysgit, /dev/stdinне існує, тому вам доведеться замінити /dev/stdinна "-" єдиний дефіс без лапок, тому повинні працювати наступні команди: $ sed 's/oo/u/' fubar | diff -p fubar -і$ sed 's/oo/u/' fubar | diff -p fubar - | patch
Jim Raden

@ AdrianFrühwirth У мене є синя поліцейська скринька десь навколо, і чомусь вони мене називають лікарем на роботі. Але, чесно кажучи, схоже на те, що в цей час у системі дійсно поганий дрейф годинника.
Дуайт Спенсер

Ці рішення дивляться на мене, спираючись на особливу буферну поведінку. Я підозрюю, що більші файли можуть зламатися, оскільки під час читання sed файл може почати змінюватися під ним командою patch, або ще гірше командою tee, яка врізає файл. Насправді я не впевнений, чи patchне вріжеться також. Я думаю, що збереження виводу в інший файл і потім catвведення в оригінал було б безпечнішим.
акостадінов

реальна відповідь на місці від @ william-pursell
akostadinov

11

Не можна робити те, що ти хочеш 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

має бути адекватним для більшості застосувань


1
так, наприклад, як би ви назвали цей скрипт і як би ви його назвали?
амфібій

2
Я б закликав це inlineі inline inputfile sed 's/foo/bar/g'
попросив

1
вау, edтільки відповідь, що дійсно є на місці. Перший раз, коли мені це потрібно ed. Дякую. Невелика оптимізація полягає у використанні echo -e ",s/foo/bar/g\012 w"або echo $',s/foo/bar/g\012 w'там, де оболонка дозволяє їй уникнути зайвих trдзвінків.
акостадінов

2
edнасправді не проводить модифікації на місці. З straceвиводу GNU edстворює тимчасовий файл, записує зміни у тимчасовий файл, потім переписує весь вихідний файл, зберігаючи жорсткі посилання. З trussвиводу, Solaris 11 edтакож використовує тимчасовий файл, але після збереження за допомогою wкоманди перейменує тимчасовий файл у вихідне ім'я файлу , знищуючи будь-які жорсткі посилання.
Ендрю Генле

@AndrewHenle Це перешкоджає. Те, edщо постачається з нинішніми мако (Mojave, що б не означав маркетинговий жаргон), досі зберігає жорсткі посилання. YMMV
Вільям Перселл

10

Ви можете використовувати vi

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

9

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зі старим змістом.

Якщо ви не хочете зберігати копію, просто не передайте параметр розширення.


3
ОП конкретно зазначає, що його платформа не підтримує цей (популярний, але) нестандартний варіант.
трійка

3

Ви не вказали, яку оболонку використовуєте, але за допомогою zsh ви можете використати =( )конструкцію для досягнення цього. Щось у напрямку:

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

=( )подібний, >( )але створює тимчасовий файл, який автоматично видаляється при cpзавершенні.


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

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


3

Якщо ви замінюєте однакову кількість символів і після ретельного прочитання редагування файлів "на місці" ...

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

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

З Довідкового посібника Баша → 3.6.10 Відкриття дескрипторів файлів для читання та запису :

Оператор перенаправлення

[n]<>word

викликає відкриття файлу, ім'я якого є розширення слова, як для читання, так і для запису на дескрипторі n або на дескрипторі 0, якщо n не вказано. Якщо файл не існує, він створюється.


Це пошкоджено файлом. Я не впевнений у тому, що є причиною. paste.fedoraproject.org/462017
Nehal J Wani

Дивіться рядки [43-44], [88-89], [135-136]
Nehal J Wani

2
У цьому блозі пояснюється, чому цю відповідь не слід використовувати. backreference.org/2011/01/29/in-place-editing-of-files
Nehal J WANI

@NehalJWani Я довго буду читати статтю, дякую за голову! Що я не згадував у відповіді, це те, що це працює, якщо він змінює однакову кількість символів.
fedorqui 'ТАК перестаньте шкодити'

@NehalJWani з цього приводу, мені дуже шкода, якщо моя відповідь заподіяла шкоду вашим файлам. Я оновлю його з виправленнями (або видаляю при необхідності) після прочитання наданих вами посилань.
fedorqui 'ТАК перестаньте шкодити'

2

Як сказав Moneypenny у Skyfall: "Іноді найкращі старі способи". Кінкаде пізніше сказав щось подібне.

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

Щасливе редагування на місці. Додано "\ nw \ n", щоб записати файл. Вибачення за прострочення відповіді на запит.


Чи можете ви пояснити, що це робить?
Метт Монтаг

1
Він викликає ed (1), текстовий редактор з деякими символами, написаними на stdin. Як хтось у віці до 30 років, я думаю, що це нормально, щоб сказати, що ed (1) - це динозаври, як і для нас динозаври. У будь-якому випадку, замість того, щоб відкривати ed та вводити цей матеріал, jlettvin пересилає символів у використанні |. @MattMontag
Ari Sweedler

Якщо ви запитуєте, що роблять команди, є дві з них, закінчені символом a \n. [діапазон] s / <old> / <new> / <flag> - це форма команди-замінника - парадигма, яка все ще спостерігається у багатьох інших текстових редакторах (добре, vim, прямий нащадок редагу). Так чи інакше, gпрапор означає "глобальний" і означає "Кожен екземпляр у кожному рядку, який ви дивитесь". Діапазон 3,5розглядає лінії 3-5. ,5дивиться на StartOfFile-5, 3,дивиться на лінії 3-EndOfFile і ,дивиться на StartOfFile-EndOfFile. Глобальна команда заміщення у кожному рядку, яка замінює "false" на "true". Потім wвводиться команда write,.
Ari Sweedler

1

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

find . -name pkg.json -print -exec sed -i '.bak' '1 s/^/version /' {} \;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.