Есть ли альтернатива команде «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также, но решение в Solaris 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 я не знаком с этим. Кстати, я видел настройки, где 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
Джефф Шаллер

1
Вы можете сделать это, если вам не нужны жесткие ссылки - посмотрите мой пример. Там все еще есть временный файл, но он скрыт (без имени). Без временного, вам нужно использовать ed, так как он читает весь файл в память.
Тоби Спейт

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\команда, на которую вы ссылаетесь, предназначена для вставки строки текста, а не для сохранения отредактированного текста обратно в файл. Для этого не существует способа использования POSIXsed .

Что вы можете сделать , это использовать 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. Существует небольшая вероятность того, что ваш исходный файл будет потерян, если ваш shell / script / 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.