Чи є альтернатива команді «sed -i» в Solaris?


11

У моєму проекті є вимога замінити деякий існуючий текст у файлі, як-от fooінший, наприклад fooofoo:

abc.txt
name
foo
foo1

Тому я спробував:

sed -i "s/foo/fooofoo/g" abc.txt

Однак я отримую цю помилку:

sed: незаконний варіант - i

У керівництві я знайшов:

sed -i\ "s/foo/fooofoo/g" abc.txt

Однак і це не працює.

Я знайшов альтернативи в, perlа awkтакож, але рішення в Соляріс sedбуде дуже вдячний.

Я використовую цю версію bash:

GNU bash, версія 3.2.57 (1) -випуск (sparc-sun-solaris2.10)


У програмі Solaris 11 та пізніших версіях просто скористайтеся, /usr/gnu/bin/sedщоб отримати підтримку -i.
alanc

Відповіді:


15

Використовуйте ed. Він доступний на більшості платформ, і він може редагувати ваші файли на місці.
Оскільки sedзаснований на edсинтаксисі заміни шаблонів подібний:

ed -s infile <<\IN
,s/old/new/g
w
q
IN

Чому edзамість ex, просто цікаво? (Я знаю, що обидва сумісні з POSIX, і я пов’язаний із специфікаціями.))
Wildcard

1
@Wildcard - я можу запитати навпаки - чому ex? Щоб відповісти на ваше запитання: оскільки я ніколи не використовую, ex я не знайомий з цим. btw, я бачив установки, де edбув, але exне був (але не навпаки) ... найпомітнішим був мій ноутбук :)
don_crissti

Хороша відповідь. :) Моя відповідь: Оскільки я весь час використовую Vim, я дуже добре знайомий з exкомандами. Усі viкоманди, які ви можете ввести, які починаються двокрапкою, - це exкоманди. Звичайно, є декілька розширень, характерних для Vim, але лише основний набір команд неймовірно потужний та гнучкий і має багато для редагування сценаріїв.
Wildcard

У моїй системі Manjaro exще немає ed.
середина

8

Якщо ви не можете встановити GNU sed, використовуйте:

sed "s/foo/fooofoo/g" abc.txt >abc.tmp && mv abc.tmp abc.txt

При цьому використовується перенаправлення для надсилання виводу sed у тимчасовий файл. Якщо sed успішно завершується, це перезаписується abc.txtтимчасовим файлом.

Як видно з вихідного коду для GNU sed , саме це sed -iробить. Отже, це майже так само ефективно sed -i.

Якщо є вже ймовірність abc.tmp, ви можете скористатися mktempабо подібною утилітою для створення унікального імені для тимчасового.


Дякуємо за допомогу. Однак чи є опція в solaris, за допомогою якої ми можемо оновити існуючий файл без створення тимчасового файлу?
tpsaitwal

10
Ненавиджу його зламати, але навіть sed створює тимчасовий файл. git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c#n84
Jeff Schaller

1
Ви можете це зробити, якщо вас не хвилюють жорсткі посилання - дивіться мій приклад. Є ще тимчасовий файл, але він прихований (без назви). Без тимчасового вам потрібно буде користуватися ed, оскільки він зчитує весь файл в пам'яті.
Toby Speight

3

Якщо ви хочете еквівалент sed -i.bak, це досить просто.

Розглянемо цей сценарій для GNU sed:

#!/bin/sh

# Create an input file to demonstrate
trap 'rm -r "$dir"' EXIT
dir=$(mktemp -d)
grep -v '[[:upper:][:punct:]]' /usr/share/dict/words | head >"$dir/foo"

# sed program - removes 'aardvark' and 'aardvarks'
script='/aard/d'

##########
# What we want to do
sed -i.bak -e "$script" "$dir"
##########

# Prove that it worked
ls "$dir"
cat "$dir/foo"

Ми можемо просто замінити позначений рядок на

cp "$dir/foo" "$dir/foo.bak" && sed -e "$script" "$dir/foo.bak" >"$dir/foo"

Це переміщує існуючий файл як резервну копію і записує новий файл.

Якщо ми хочемо еквівалент

sed -i -e "$script" "$dir"  # no backup

то це трохи складніше. Ми можемо відкрити файл для читання як стандартний вхід, а потім від’єднати його, перш ніж направити вихід sed на його заміну:

( cp "$dir/foo" "$dir/foo.bak"; exec <"$dir/foo.bak"; rm "$dir/foo.bak"; exec sed -e "$script" >"$dir/foo" )

Ми робимо це в підрозділі, щоб наш оригінал stdin все ще був доступний після цього. Можливо перемикати входи та повертатися назад без додаткової оболонки, але цей спосіб мені здається яснішим.

Зауважте, що ми ретельно копіюємо спочатку, а не створюємо новий fooфайл - це важливо, якщо файл відомий більш ніж одним ім'ям (тобто має жорсткі посилання) і ви хочете бути впевнені, що ви не перервите посилання .


3

По-перше, ви повинні знати, що i\команда, на яку ви посилаєтесь, - це вставити рядок тексту, а не для збереження відредагованого тексту назад у файл. Існує не визначений POSIX спосіб використовувати sedдля цього.

Що ви можете зробити , це використовувати ex, який буде вказано в POSIX :

printf '%s\n' '%s/find/replace/g' 'x' | ex file.txt

xКоманда для «зберегти і вийти.» Ви також можете використовувати, wqале xкоротше.

%Знак на початку кошти команди заміни, «Застосувати цю команду для кожного рядка в буфері.» Ви можете 1,$s/find/replace/gтакож використовувати .


Одна з головних відмінностей між exі sedполягає в тому, що sedце редактор потоків . Він діє лише послідовно, рядок за рядком. exнабагато гнучкіший за це, і ви можете робити інтерактивне редагування текстових файлів безпосередньо в ex. Це безпосередній попередник vi.


1

Використання sedта не видимий тимчасовий файл:

Ви можете уникнути створення окремого видимого "темп-файлу":

exec 3<abc.txt
rm abc.txt
sed 's/foo/fooofoo/' <&3 >abc.txt
exec 3<&-

Пояснення

Unix-подібні системи на самому ділі не видалити вміст файлу з диска , поки це не як непов'язаний в файлової системі, а не відкривати в будь-якому процесі. Таким чином, ви можете зробити, exec 3<щоб відкрити файл у оболонці для читання у файлі дескриптора 3, rmфайл (який від’єднує його від файлової системи), а потім викликати sedдескриптор файлу 3, який використовується як його вхід.

Зауважте, що це сильно відрізняється від цього:

# Does not work.
sed 's/foo/fooofoo/' <abc.txt >abc.txt

Різниця полягає в тому, що, виконуючи це в одній команді, оболонка просто відкриває один і той же файл і для читання, і для запису з можливістю усікати файл - оскільки це все-таки той самий файл, ви втрачаєте вміст. Але якщо ви відкриєте його для читання, то rmце, потім відкрийте те саме ім'я шляху для запису, ви фактично створюєте новий файл з тим же ім'ям шляху (але в новому місці вводу та диска, оскільки оригінал все ще відкритий): так вміст все ще доступний.

Потім, коли ви закінчите, ви можете закрити дескриптор файлів, який ви відкрили раніше (саме це робить exec 3<&-спеціальний синтаксис), який випускає оригінальний файл, щоб операційна система могла видалити (позначити як невикористаний) свій диск на диску.

Коваджі

Про це рішення слід пам’ятати кілька речей:.

  1. Ви отримуєте лише один "перехід" через вміст - у дескрипторі файлів немає переносного способу, щоб оболонка "шукала" - тому, як тільки програма прочитає частину вмісту, інші програми побачать лише решту файлу. І sedпрочитає весь файл.

  2. Існує невеликий шанс, що ваш оригінальний файл буде загублений, якщо ваша оболонка / скрипт / sed буде вбита, перш ніж це буде зроблено.


Щоб було зрозуміло, @TobySpeight вже розмістив приклад цього рішення наприкінці своєї відповіді, але я подумав, що це може використовувати більш глибоку розробку.
mtraceur

Для прихильників: Хоча я впевнений, що лише за те, щоб залишити шлюб, ви відчуваєте себе добре, я б вдячний, якщо ви докладете більше, надавши відгуки про те, які проблеми у вас є з цією відповіддю, як її можна покращити тощо .
mtraceur

можливо, ви можете використовувати perl -i
c4f4t0r

@ c4f4t0r: Вибачте за пізню відповідь: Так, perl -iще один хороший варіант. Питання конкретно просив рішення , сумісне з Solaris ' sed, на відміну від рішень з perlі awkщо вони вже згадувалося знайти. Плюс, моя відповідь головним чином мала на меті додати щось, що, на мою думку, було корисно знати і пропущене в будь-якій іншій відповіді.
mtraceur
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.