59

Мне нужно заменить текст внутри текстового файла с заменой. Обычно я хотел бы сделать что-то вроде

sed -i 's/text/replacement/g' path/to/the/file

Проблема в том, что и text и replacement представляют собой сложные строки, содержащие тире, косые черты, черные черты, кавычки и так далее. Если я уберу все необходимые символы внутри text эта вещь быстро станет нечитаемой. С другой стороны, мне не нужна сила регулярных выражений: мне просто нужно заменить текст буквально.

Есть ли способ сделать подстановку текста без использования регулярных выражений с какой-либо командой bash?

Было бы довольно тривиально написать скрипт, который делает это, но я думаю, что что-то уже должно существовать.

9 ответов9

12
export FIND='find this'
export REPLACE='replace with this'
ruby -p -i -e "gsub(ENV['FIND'], ENV['REPLACE'])" path/to/file

Это единственное 100% безопасное решение здесь, потому что:

  • Это статическая подстановка, а не регулярное выражение, не нужно ничего избегать (поэтому лучше, чем использовать sed)
  • Он не сломается, если ваша строка содержит } char (таким образом, превосходит представленное решение Perl)
  • Он не будет разрываться ни с одним символом, потому что используется ENV['FIND'] , а не $FIND . С $FIND или вашим текстом, встроенным в код Ruby, вы можете получить синтаксическую ошибку, если ваша строка содержит неэкранированный ' .
7

Когда вам не нужна сила регулярных выражений, не используйте ее. Это хорошо.
Но это не совсем регулярное выражение.

sed 's|literal_pattern|replacement_string|g'

Итак, если / это ваша проблема, используйте | и вам не нужно убегать от первого.

ps: о комментариях, также посмотрите этот ответ Stackoverflow на Escape-строку для шаблона поиска sed.


Обновление: если вы хорошо используете Perl, попробуйте с \Q и \E например,
perl -pe 's|\Qliteral_pattern\E|replacement_string|g'
RedGrittyBrick также предложил подобный трюк с более сильным синтаксисом Perl в комментарии здесь

7

Команда replace сделает это.

https://linux.die.net/man/1/replace

Изменить место:

replace text replacement -- path/to/the/file

В стандартный вывод:

replace text replacement < path/to/the/file

Пример:

$ replace '.*' '[^a-z ]{1,3}' <<EOF
> r1: /.*/g
> r2: /.*/gi
> EOF
r1: /[^a-z ]{1,3}/g
r2: /[^a-z ]{1,3}/gi

Команда replace поставляется с MySQL или MariaDB.

3

Вы также можете использовать механизм Perl \Q для « цитирования (отключения) метасимволов паттернов »

perl -pe 'BEGIN {$text = q{your */text/?goes"here"}} s/\Q$text\E/replacement/g'
2

Вы можете сделать это, избегая своих шаблонов. Как это:

keyword_raw='1/2/3'
keyword_regexp="$(printf '%s' "$keyword_raw" | sed -e 's/[]\/$*.^|[]/\\&/g')"
# keyword_regexp is now '1\/2\/3'

replacement_raw='2/3/4'
replacement_regexp="$(printf '%s' "$replacement_raw" | sed -e 's/[\/&]/\\&/g')"
# replacement_regexp is now '2\/3\/4'

echo 'a/b/c/1/2/3/d/e/f' | sed -e "s/$keyword_regexp/$replacement_regexp/"
# the last command will print 'a/b/c/2/3/4/d/e/f'

Кредиты для этого решения идет здесь: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern

Примечание 1: это работает только для непустых ключевых слов. Sed не принимает пустые ключевые слова (sed -e 's//replacement/').

Примечание 2: к сожалению, я не знаю популярного инструмента, который бы НЕ использовал regexp-s для решения проблемы. Вы можете написать такой инструмент на Rust или C, но по умолчанию его нет.

2

зацени мой Perl скрипт он делает именно то, что вам нужно, без явного или явного использования регулярного выражения:

https://github.com/Samer-Al-iraqi/Linux-str_replace

str_replace Search Replace File # replace in File in place

STDIN | str_replace Search Replace # to STDOUT

очень удобно, верно? Я должен был изучить Perl, чтобы сделать это. потому что мне действительно это нужно.

1

Вы можете использовать php's str_replace:

php -R 'echo str_replace("\|!£$%&/()=?^\"'\''","replace",$argn),PHP_EOL;'<input.txt >output.txt

Примечание: Вы все еще должны были бы избежать одинарные кавычки ' двойные кавычки " хотя.

1

Я собрал несколько других ответов и придумал это:

function unregex {
   # This is a function because dealing with quotes is a pain.
   # http://stackoverflow.com/a/2705678/120999
   sed -e 's/[]\/()$*.^|[]/\\&/g' <<< "$1"
}
function fsed {
   local find=$(unregex "$1")
   local replace=$(unregex "$2")
   shift 2
   # sed -i is only supported in GNU sed.
   #sed -i "s/$find/$replace/g" "$@"
   perl -p -i -e "s/$find/$replace/g" "$@"
}
0

Узел.JS эквивалент @Nowaker:

export FNAME='moo.txt'
export FIND='search'
export REPLACE='rpl'
node -e 'fs=require("fs");fs.readFile(process.env.FNAME,"utf8",(err,data)=>{if(err!=null)throw err;fs.writeFile(process.env.FNAME,data.replace(process.env.FIND,process.env.REPLACE),"utf8",e=>{if(e!=null)throw e;});});'

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .