Заміна в текстовому файлі ** без ** регулярних виразів


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 має бути аналогічний варіант
Акаш

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

Відповіді:


6

Коли вам не потрібна сила регулярних виразів, не використовуйте її. Це добре.
Але це насправді не регулярний вираз .

sed 's|literal_pattern|replacement_string|g'

Отже, якщо /це ваша проблема, використовуйте |і вам не потрібно уникати колишнього.

ps: про коментарі, також дивіться цю відповідь Stackoverflow в розділі Escape a string for sed search pattern .


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


Дякую, я не знав про різницю між / та |
Андреа

64
Я не впевнений, що ця відповідь корисна ... Єдина відмінність s|||і s///полягає в тому, що символ сеператора відрізняється, і тому одному символу не потрібно бігти. Ви могли однаково зробити s###. Справжня проблема тут полягає в тому, що ОП не хоче турбуватися про уникнення вмісту 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)
  • Він не порушиться, якщо ваша рядок містить }char (таким чином, перевершує подане рішення Perl)
  • Він не зірветься ні з одним персонажем, тому що ENV['FIND']використовується, ні $FIND. З $FINDабо введений текст у коді Ruby, ви можете зіткнутися з помилкою синтаксису, якщо ваша рядок містить немальований розмір '.

Мені довелося використовувати export FIND='find this; export REPLACE='replace with this';в моєму скрипті bash так, щоб ENV['FIND']і ENV['replace']мали очікувані значення. Я замінював деякі дійсно довгі зашифровані рядки у файлі. Це був лише квиток.
DMfll

Це хороша відповідь, оскільки це надійно, а рубін - всюдисущий. На основі цієї відповіді я зараз використовую цей скрипт оболонки .
loevborg

На жаль, не працює, коли FIND містить кілька рядків.
адреланос

Немає нічого, що заважало б йому працювати з декількома рядками в FIND. Використовуйте подвійне цитування \ n
Nowaker

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
врахуйте, що заміна tht застаріла і може не бути одноразовою у майбутньому
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

дуже зручно так? Мені довелося навчитися Перла робити це. тому що мені це дуже потрібно.


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: на жаль, я не знаю популярного інструменту, який НЕ використовував bi regexp-s для вирішення проблеми. Ви можете написати такий інструмент в Rust або C, але його немає за замовчуванням.


Це повністю пропускає пункт ОП. Очевидно, ви можете уникнути шаблону, але для деяких моделей це втомливо.
морозивослов

@icecreamsword Ви читали мою відповідь під першим рядком? Сценарій виходить автоматично .
VasyaNovikov

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. Будь-яке рішення?
адреланос

1

Ви можете використовувати str_replace php :

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'); \\"> / мій substitustring = \\ "bac) (o: # 46; \\"> /
: х

тоді виконайте vi або vim з командного рядка:

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

:% sno - команда vi для пошуку та заміни без магії.

/ - це мій вибраний роздільник.

: x зберігає та виходить vi.

Вам потрібно уникнути косої риски "\" форвард "/" може бути замінений, наприклад, знаком питання "?" або щось інше, що не знаходиться у вашому пошуку або заміну-string, pipe '|' не працював для мене Тхо.

ref: 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.