Подстановка в текстовом файле ** без ** регулярных выражений


68

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

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

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

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

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


Нужно сделать это через bash? Упрощенным решением было бы открыть в Word и сделатьfind and replace all
Акаш

17
@akash Потому что системы, которые bashвсегда поставляются с Microsoft Word? ;) Нет, просто прикалываюсь. ОП может захотеть сделать это на удаленной машине или для пакета файлов, хотя.
slhck

@slhck :) Ну, я думаю, у gedit должна быть похожая опция
Akash

Можно было бы как-то правильно избежать всего перед тем, как передать его sed, что, вероятно, бесполезно, учитывая все различия между коммутаторами и платформами.
10

Ответы:


6

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

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 в комментарии здесь


Спасибо, я не знал о разнице между / и |
Андреа

64
Я не уверен, что этот ответ полезен ... Единственная разница между s|||и s///заключается в том, что разделительный символ отличается и поэтому один символ не нуждается в экранировании. Вы могли бы в равной степени сделать s###. Реальная проблема здесь в том, что OP не хочет беспокоиться о экранировании содержимого literal_pattern(которое не является буквальным и будет интерпретироваться как регулярное выражение).
Бендж

15
Это не позволит избежать интерпретации других специальных символов. Что делать, если поиск 1234.*aaaс вашим решением будет соответствовать гораздо больше, чем предполагалось 1234\.\*aaa.
Маттео

20
Этот ответ не должен быть принят
Стивен Лу

2
Это полностью упускает из виду. Текст для сопоставления может содержать любые странности. В моем случае это случайный пароль. Вы знаете, как они идут
Кристиан Бонджорно

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

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

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

Мне пришлось использовать export FIND='find this; export REPLACE='replace with this';в моем bash-скрипте так, чтобы ENV['FIND']и ENV['replace']имел ожидаемые значения. Я заменял некоторые действительно длинные зашифрованные строки в файле. Это был просто билет.
DMfll

Это хороший ответ, потому что он надежный, а ruby ​​- вездесущий. Основываясь на этом ответе, я теперь использую этот сценарий оболочки .
loevborg

К сожалению, не работает, когда FIND содержит несколько строк.
adrelanos

Нет ничего, что могло бы помешать ему работать с несколькими строками в FIND. Используйте двойные кавычки \ n.
Новакер

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
принять во внимание, что замена устарела и, возможно, не будет лишена в будущем
Rogelio

1
С какой стати такая основная команда приходит с базой данных?
masterxilo

3
@masterxilo Лучший вопрос может быть - почему такая базовая команда не поставляется с современными операционными системами? ;-)
Марк Томсон


3

зацени мой 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, чтобы сделать это. потому что мне действительно это нужно.


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, но его нет по умолчанию.


Это полностью пропускает точку зрения ОП. Очевидно, вы можете избежать шаблона, но для некоторых шаблонов это утомительно.
Ледяной

@icecreamsword вы читали мой ответ ниже первой строки? Скрипт автоматически убегает .
ВасяНовиков

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" "$@"
}

Не работает с переводами строки. Также не помогает избежать перевода строки \n. Любое решение?
Adrelanos

1

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

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

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


0

Node.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;});});'

0

Вот еще один «почти» рабочий путь.

Используйте vi или vim.

Создайте текстовый файл с вашей заменой:

:% sno / моя строка поиска \\ "-: # 2; g ('. j'); \\"> / my replacestring = \\ "bac) (o: # 46; \\"> /
:Икс

затем выполните vi или vim из командной строки:

vi -S commandfile.txt path/to/the/file

:% sno - команда vi для поиска и замены без магии.

/ мой выбранный разделитель.

: x сохраняет и выходит из vi.

Вы должны избегать обратной косой черты '\' прямая косая черта '/' может быть заменена, например, вопросительным знаком '?' или что-то еще, чего нет в вашей строке поиска или замены, конвейер '|' не работал для меня, хотя.

ссылка: https://stackoverflow.com/questions/6254820/perform-a-non-regex-search-replace-in-vim https://vim.fandom.com/wiki/Search_without_need_to_escape_slash http://linuxcommand.org/ lc3_man_pages / vim1.html

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.