
В моем проекте есть требование заменить некоторый существующий текст в файле, например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)
решение1
Использоватьed
. Он доступен на большинстве платформ и может редактировать ваши файлы на месте.
Поскольку sed
он основан на ed
синтаксисе для замены шаблонов, он похож:
ed -s infile <<\IN
,s/old/new/g
w
q
IN
решение2
Если вы не можете установить 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
или аналогичную утилиту для генерации уникального имени для временного файла.
решение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
файл, а не создавать новый — это важно, если файл известен под несколькими именами (т. е. имеет жесткие ссылки) и вы хотите быть уверены, что не нарушите ссылки.
решение4
Использование 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<&-
делает специальный синтаксис), что освобождает исходный файл, чтобы операционная система могла удалить (отметить как неиспользуемое) его дисковое пространство.
Предостережения
При использовании этого решения следует учитывать несколько моментов:
У вас есть только один «проход» по содержимому — нетпортативныйспособ для оболочки "искать" обратно в дескрипторе файла - так что как только программа прочитает часть содержимого, другие программы увидят только остаток файла. И
sed
прочитают весь файл.Существует небольшая вероятность того, что исходный файл будет утерян, если ваша оболочка/скрипт/sed будет завершена до ее завершения.