- Вопрос или проблема
- Заменить (литеральные) строки в переменной bash
- Загрузка текста в переменную bash без экранирования
- Одиночные кавычки
- Here-документы
- Файлы
- Загрузка данных из файла
- Запись данных в файл
- Сводим всё воедино
- Использование простого Python-скрипта
- Обработка произвольных буквенных символов
- 1. Записать символы на диск
- 2. Используйте модифицированный Python-скрипт
- !!! ОДИН-СТРОЧНИК !!!
- Ответ или решение
- 1. Использование встроенной замены в Bash
- 2. Использование sed
- 3. Использование perl с экранированием
- 4. Использование Python
- 5. Использование ruby
- Заключение
Вопрос или проблема
Мне нужно заменить некоторый текст в текстовом файле на другой текст. Обычно я делаю что-то вроде этого:
sed -i 's/text/replacement/g' путь/к/файлу
Проблема в том, что как text
, так и replacement
являются сложными строками, содержащими дефисы, косые черты, обратные слэши, кавычки и так далее. Если я экранирую все необходимые символы в text
, это быстро становится неразборчивым. С другой стороны, мне не нужны возможности регулярных выражений: я просто хочу заменить текст буквально.
Существует ли способ выполнить замену текста без использования регулярных выражений с помощью какой-то команды bash?
Написать скрипт, который это делает, было бы довольно тривиально, но мне кажется, что что-то подобное уже должно существовать.
Когда вам не нужны возможности регулярных выражений, не используйте их. Это нормально.
Но это не совсем регулярное выражение.
sed 's|literal_pattern|replacement_string|g'
Так что, если /
представляет собой вашу проблему, используйте |
, и вам не нужно экранировать первый.
PS: Что касается комментариев, также смотрите этот ответ Stackoverflow о экранировании строки для поискового шаблона sed.
Обновление: Если вас устраивает использование Perl, попробуйте с \Q
и \E
вот так:
perl -pe 's|\Qliteral_pattern\E|replacement_string|g'
@RedGrittyBrick также предложил подобный трюк с более сильным синтаксисом Perl в комментарии здесь или здесь
export FIND='найти это'
export REPLACE='заменить на это'
ruby -p -i -e "gsub(ENV['FIND'], ENV['REPLACE'])" путь/к/файлу
Это единственное 100% безопасное решение здесь, потому что:
- Это статическая замена, а не регулярное выражение, не нужно ничего экранировать (поэтому, это превосходит использование
sed
) - Это не сломается, если ваша строка содержит символ
}
(поэтому, это превосходит предложенное решение на Perl) - Это не сломается ни с каким символом, потому что используется
ENV['FIND']
, а не$FIND
. При использовании$FIND
или текста, встроенного в код Ruby, вы можете получить синтаксическую ошибку, если ваша строка содержит неэкранированную'
.
Команда replace
выполнит это действие.
https://linux.die.net/man/1/replace
Замена на месте:
replace text replacement -- путь/к/файлу
Для stdout:
replace text replacement < путь/к/файлу
Пример:
$ 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.
Вы можете сделать это, автоматически преобразуя шаблоны в их экранированную форму. Вот так:
keyword_raw=$'1\n2\n3'
keyword_regexp="$(printf '%s' "$keyword_raw" | sed -e 's/[]\/$*.^|[]/\\&/g' | sed ':a;N;$!ba;s,\n,\\n,g')"
# keyword_regexp теперь '1\/2\/3'
replacement_raw=$'2\n3\n4'
replacement_regexp="$(printf '%s' "$replacement_raw" | sed -e 's/[\/&]/\\&/g' | sed ':a;N;$!ba;s,\n,\\n,g')"
# replacement_regexp теперь '2\/3\/4'
echo $'a/b/c/1\n2\n3/d/e/f' | sed -e "s/$keyword_regexp/$replacement_regexp/"
# последняя команда напечатает 'a/b/c/2\n3\n4/d/e/f'
Кредиты за это решение здесь: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern
Примечание 1: это работает только для непустых ключевых слов. Пустые ключевые слова не принимаются sed (sed -e 's//replacement/'
).
Примечание 2: к сожалению, я не знаю популярного инструмента, который бы не использовал регулярные выражения для решения этой проблемы. Вы можете написать такой инструмент на Rust или C, но его там нет по умолчанию.
Вы также можете использовать механизм \Q
в perl для “цитирования (деактивации) метасимволов шаблона“
perl -pe 'BEGIN {$text = q{ваш */text/?идет"сюда"}} s/\Q$text\E/replacement/g'
посмотрите мой Perl-скрипт. Он делает именно то, что вам нужно, без неявного или явного использования регулярных выражений:
https://github.com/Samer-Al-iraqi/Linux-str_replace
str_replace Поиск Замена Файл # замена в файле на месте
STDIN | str_replace Поиск Замена # в STDOUT
очень удобно, не правда ли? Мне пришлось учить Perl, чтобы сделать это. потому что мне это действительно нужно.
Это улучшение ответа Hashbrown
(и ответа wef
на очень похожий вопрос).
Мы можем устранить проблему специального значения различных специальных символов
и строк (^
, .
, [
, *
, $
, \(
, \)
, \{
, \}
, \+
, \?
,
&
, \1
, …, что угодно, и разделитель /
)
убрав специальные символы.
В частности, мы можем преобразовать все в шестнадцатеричный вид;
тогда у нас останется только 0
–9
и a
–f
для обработки.
Этот пример демонстрирует принцип:
$ echo -n '3.14' | xxd
0000000: 332e 3134 3.14
$ echo -n 'pi' | xxd
0000000: 7069 pi
$ echo '3.14 is a transcendental number. 3614 is an integer.' | xxd
0000000: 332e 3134 2069 7320 6120 7472 616e 7363 3.14 is a transc
0000010: 656e 6465 6e74 616c 206e 756d 6265 722e endental number.
0000020: 2020 3336 3134 2069 7320 616e 2069 6e74 3614 is an int
0000030: 6567 6572 2e0a eger..
$ echo "3.14 is a transcendental number. 3614 is an integer." | xxd -p |
sed 's/332e3134/7069/g' | xxd -p -r
pi is a transcendental number. 3614 is an integer.
в то время как, конечно, sed 's/3.14/pi/g'
также изменит 3614
.
Вышеизложенное является небольшим упрощением; это не учитывает границы.
Рассмотрим этот (в некоторой степени искусственный) пример:
$ echo -n 'E' | xxd
0000000: 45 E
$ echo -n 'g' | xxd
0000000: 67 g
$ echo '$Q Eak!' | xxd
0000000: 2451 2045 616b 210a $Q Eak!.
$ echo '$Q Eak!' | xxd -p | sed 's/45/67/g' | xxd -p -r
&q gak!
Поскольку $
(24
) и Q
(51
)
сочетаются для формирования 2451
,
команда s/45/67/g
разрывает это изнутри.
Она изменяет 2451
на 2671
, что соответствует &q
(26
+ 71
).
Мы можем предотвратить это, отделив байты данных в тексте поиска,
в тексте замены и в файле пробелами.
Вот стильное решение:
encode() {
xxd -p -- "$@" | sed 's/../& /g' | tr -d '\n'
}
decode() {
xxd -p -r -- "$@"
}
left=$( printf '%s' "$search" | encode)
right=$(printf '%s' "$replacement" | encode)
encode путь/к/файлу | sed "s/$left/$right/g" | decode
Я определил функцию encode
, потому что использовал эту функциональность три раза,
а затем определил decode
для симметрии.
Если вы не хотите определять функцию decode
, просто измените последнюю строку на
encode путь/к/файлу | sed "s/$left/$right/g" | xxd -p –r
Обратите внимание, что функция encode
утраивает размер данных (текста)
в файле и затем отправляет его через sed
как одну строку
— даже без новой строки в конце.
GNU sed, похоже, может с этим справиться;
другие версии могут не справиться с этим.
Кроме того, это не изменяет файл на месте;
вам нужно будет записать вывод во временный файл
и затем переименовать его в оригинальный файл
(или воспользоваться одним из других трюков для этого).
В качестве дополнительного бонуса, это решение обрабатывает многократный поиск и замену
(другими словами, строки поиска и замены, которые содержат новую строку(ы)).
Вы можете использовать str_replace в php:
php -R 'echo str_replace("\|!£$%&/()=?^\"'\''","замена",$argn),PHP_EOL;'<input.txt >output.txt
Примечание: Вам всё равно нужно будет экранировать одинарные кавычки '
и двойные кавычки "
.
Работая в контейнере alpine docker, мне не хотелось устанавливать python / pearl / ruby / python, просто чтобы выполнить очень простую операцию поиска и замены. Все эти решения ужасно сложны!!
Существует два жизнеспособных решения этой проблемы:
- Используйте другой метод поиска и замены из другого места (например, python/pearl/и т.д.)
- Экранируйте все специальные символы регулярных выражений. Мы можем использовать sed для этой цели.
Я не могу сделать первое в своем случае, так как работаю в минимальном контейнере docker.
Это решение может быть использовано для второго
В моем случае у меня была известная строка в файле: {{replace_me}}
и ввод пользователя. Назовем его $replace_text
.
sed -i "s/{{replace_me}}/$(sed 's/[&/\]/\\&/g' <<<"$replace_text")/g" путь/к/файлу
Как это работает?
Мы используем sed -i
для преобразования на месте. Здесь я использую \
в качестве разделителя, так как конкретно экранирую это в строке замены. Это защищает от вставки пользователем my\string
.
Часть $(sed 's/[&/\]/\\&/g' <<<"$replace_text")
подробно объяснена здесь в отличном ответе, откуда взято это решение. В этом случае я использую это как однострочник
В ответ на начальный вопрос ОП вот однострочник sed, который должен сработать:
sed -i "s/$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$search_text")/$(sed 's/[&/\]/\\&/g' <<<"$replace_text")/g" путь/к/файлу
но, я предполагаю, что ему, вероятно, уже не важно, учитывая, что прошло 7 лет!
Заменить (литеральные) строки в переменной bash
Учитывая переменные bash, содержащие:
- оригинальные данные,
- текст для замены, и
- текст замены
литеральная замена строк может быть выполнена с помощью:
modified=${original/"$text"/"$replacement"}
Двойные кавычки обязательны.
Смотрите Shell Parameter Expansion для подробностей.
Сложная часть заключается в том, чтобы правильно поместить всё в переменные без экранирования.
Загрузка текста в переменную bash без экранирования
Чтобы избежать любого цитирования, следует выбрать разделитель, который не может появиться в строке. Это в общем случае невозможно, но экранирование можно минимизировать.
Одиночные кавычки
Если одиночные кавычки ('
) являются разделителем, то любой символ кроме одиночной кавычки может появляться в строке:
var="это переменная
содержащая много всего !@#$%^&*()-=_+[]{};\:"|,./<>?
а также конечные (и другие) новые строки:
"
К сожалению, одиночные кавычки всё ещё необходимо экранировать и включать отдельно. Например:
var="всё, кроме одиночной кавычки ("\'')'
Here-документы
Другой метод — это чтение в переменную из here-документа. В этом случае последовательность разделителей не может появляться (EOD
ниже). Обязательно используйте кавычки вокруг разделителя, иначе ввод будет обрабатываться как строка с двойными кавычками, и могут выполняться расширения.
var=$(cat <<'EOD'
это переменная
содержащая много всего !@#$%^&*()-=_+[]{};\:"|,./<>?
включая одиночные кавычки: '''
Разделитель (`EOD`) не может появляться отдельно в строке.
EOD
)
Однако обратите внимание, что с этим методом любые завершающие новые строки не сохраняются. Чтобы включить их, можно использовать mapfile
, если он доступен (bash 4+) или можно добавить и удалить отправное значение:
mapfile -d '' var <<'EOD'
Все разрешено (кроме `EOD` отдельно в строке).
Завершающие новые строки сохраняются:
EOD
tmpvar=$(cat <<'EOD'
Строка, содержащая что угодно (кроме `EOD` отдельно в строке).
Завершающие новые строки не могут существовать из-за отправного значения:
отправное значение
EOD
)
var=${tmpvar%отправное значение}
Файлы
Если содержимое для переменной хранится в фактическом файле, то разделителем может быть нулевой символ (который не может храниться в переменной bash и поэтому гарантированно не появится). Смотрите ниже.
Загрузка данных из файла
Если оригинальные данные хранятся в файле, а не в переменной, их необходимо сначала прочитать. В версиях bash, которые поддерживают mapfile
, это можно безопасно сделать с помощью:
mapfile -d '' var <"$path_to_file"
В противном случае можно использовать трюк с отправным значением:
tmpvar=$(cat "$path_to_file"; echo sentinel)
var=${tmpvar%sentinel}
Если завершающие новые строки не существуют (или должны быть удалены), bash
предоставляет немного более быстрый вариант для cat
для чтения из файла:
var=$(< "$path_to_file")
Обратите внимание, что эти методы также могут использоваться для загрузки переменных text
и replacement
с произвольным текстом, хранящимся в файлах.
Запись данных в файл
Сохранить измененные данные можно с помощью printf
:
printf '%s' "$modified" >"$path_to_file"
printf
более надежен, чем echo
, потому что различные реализации echo
ведут себя по-разному, и напечатанная строка может быть искажена. Например, рассмотрим modified='-n'; echo "$modified"
или если modified
по каким-то причинам не должен заканчиваться новой строкой.
Смотрите: https://unix.stackexchange.com/q/65803/333919
Сводим всё воедино
mapfile -d '' text <<'EOD'
...что угодно (кроме EOD отдельно в строке)...
...(или используйте трюк с отправным значением (без отправного значения!), если текст не
... завершается новой строкой; или строку с одинарными кавычками, если это проще)
EOD
mapfile -d '' replacement <<'EOD'
...что угодно (кроме EOD отдельно в строке)..
...(снова, используйте трюк с отправным значением, если обработка новой строки не требуется)
EOD
mapfile -d '' original <"$path_to_file"
printf '%s' "${original/"$text"/"$replacement"}" >"$path_to_file"
Я собрал несколько других ответов и пришел к этому:
function unregex {
# Это функция, потому что иметь дело с кавычками — это боль.
# 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 поддерживается только в GNU sed.
#sed -i "s/$find/$replace/g" "$@"
perl -p -i -e "s/$find/$replace/g" "$@"
}
Вы можете сделать это в sh без какого-либо скрипта (хотя лучше было бы поместить этот “однострочник” в скрипт) или нестандартной внешней программы (мне очень понравился ответ @Nowaker благодаря его безопасности против инъекций, но этот старый CentOS, на котором мне это понадобилось, не имел ruby!). при условии, что perl
не является нестандартным для вас
Не пытаясь экранировать строку (и учитывать проблемы с правильной синтаксической экранировкой, зная все специальные символы и так далее), мы можем просто охватить кодировать все строки, чтобы ничто не могло быть специальным.
cat путь/к/файлу | xxd -p | tr -d '\n' \
| perl -pe "s/$(printf '%s' 'text' | xxd -p | tr -d '\n')(?=(?:.{2})*\$)/$(printf '%s' 'replacement' | xxd -p | tr -d '\n')/g" \
| xxd -p -r
Это было просто чтобы соответствовать примеру задающего вопрос, другие пользователи могут, очевидно, заменить 'text'
на "$text"
, если используют переменную, или cat путь/к/файлу
на printf '%s' "$input"
, если не используют файл.
Вы даже можете заменить /g
на /
, чтобы сделать замену единожды, или изменить регулярное выражение вне $()
, чтобы “экранировать” только части сопоставителя (скажем, добавить ^
после s/
, чтобы сделать его соответствующим только началу файла).
Если вам нужно, чтобы ^/$ соответствовали концам строк, вам снова придется их декодировать:
cat путь/к/файлу | xxd -p | tr -d '\n' | sed 's/0a/\n/g'\
| perl -pe "s/^$(printf '%s' 'text' | xxd -p | tr -d '\n')(?=(?:.{2})*\$)/$(printf '%s' 'replacement' | xxd -p | tr -d '\n')/g" \
| sed 's/\n/0a/g' | xxd -p -r
Это заменит все строки в файле, начинающиеся с ‘text’, чтобы вместо этого начинаться с ‘replacement’
Тест:
Внутри ^/.[a]|$0\\{7}!!^/.[a]|$0\\{7}!!^/.[a]|$0\\{7}
, замените литерал ^/.[a]|$0\\{7}
на литерал $0\\
printf '%s' '^/.[a]|$0\\{7}!!^/.[a]|$0\\{7}!!^/.[a]|$0\\{7}' \
| xxd -p | tr -d '\n' \
| perl -pe "s/$(printf '%s' '^/.[a]|$0\\{7}' | xxd -p | tr -d '\n')(?=(?:.{2})*\$)/$(printf '%s' '$0\\' | xxd -p | tr -d '\n')/g" \
| xxd -p -r
Выход:
$0\\!!$0\\!!$0\\
После изучения множества ответов здесь и не найдя простого способа выполнить поиск + замену с текстовыми литералами (не регулярные выражения) с помощью sed
/ git grep
:
Я написал небольшой инструмент CLI для этого:
go install -v github.com/paralin/git-find-replace@main
cd ./my/git/repo
git find-replace 'SearchString' 'ReplaceString'
Исходный код инструмента доступен на GitHub.
Эквивалент 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;});});'
Вот еще один “почти” рабочий способ.
Используйте vi или vim.
Создайте текстовый файл с вашей заменой:
:%sno/my search string \\"-:#2;g('.j');\\">/my replacestring=\\"bac)(o:#46;\\">/ :x
затем выполните vi или vim из командной строки:
vi -S commandfile.txt путь/к/файлу
:%sno — это команда vi для выполнения поиска и замены без использования магических символов.
/ — это мой выбранный разделитель.
😡 сохраняет и выходит из vi.
Вам нужно экранировать обратные слэши ‘\’, прямой слеш “https://superuser.com/” может быть заменен, например, вопросительным знаком ‘?’ или чем-то другим, что не содержится в вашем поисково-заменительном выражении, но ” | ” не сработал для меня.
ссылки: 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
Использование простого Python-скрипта
В наши дни на большинстве систем готов python. Вот простой скрипт, который вам подойдет:
# replace.py
# ИСПОЛЬЗОВАНИЕ: python replace.py плохое-слово хорошее-слово целевой-файл.txt
#
import sys
search_term = sys.argv[1]
replace_term = sys.argv[2]
target_file = sys.argv[3]
with open(target_file, 'r') as file:
content = file.read()
content = content.replace(sys.argv[1], sys.argv[2])
with open(target_file, 'w') as file:
file.write(content)
Одно предостережение: это работает отлично, если ваши хорошие/плохие слова уже записаны в системных/оконных переменных.
Просто убедитесь, что вы используете двойные кавычки для окружения переменных при передаче в скрипт.
Например:
python replace.py "$BAD_WORD" "$GOOD_WORD" целевой-файл.txt
Однако это не сработает:
# Это ломается на $ или " символах
BAD_WORD="ваша-арбитрарная-строка"
# Это ломается на ' символах
BAD_WORD='ваша-арбитрарная-строка'
# Это ломается на пробелы и множество символов
BAD_WORD=ваша-арбитрарная-строка
Обработка произвольных буквенных символов
1. Записать символы на диск
Если мне нужно передать произвольное литеральное значение скрипту (пропуская любые экранирования), я, как правило, записываю его на диск следующим образом:
head -c -1 << 'CRAZY_LONG_EOF_MARKER' | tee /path/to/file > /dev/null
произвольно-однострочное-значение
CRAZY_LONG_EOF_MARKER
… где:
- Мы используем механизм Here Document для записи литерального текста
- Мы используем
head
иtee
, чтобы удалить завершающую новую строку, которую создают here-документы - Мы предотвращаем преобразование переменных внутри here-документа, цитируя строку EOL
Вот быстрое демо со сложными символами:
head -c -1 << 'CRAZY_LONG_EOF_MARKER' | tee /path/to/file > /dev/null
1"2<3>4&5'6$7 # 8
CRAZY_LONG_EOF_MARKER
2. Используйте модифицированный Python-скрипт
Вот обновленный скрипт, который читает из текстовых файлов:
# replace.py
# ИСПОЛЬЗОВАНИЕ: python replace.py bad-word.txt good-word.txt целевой-файл.txt
#
import sys
search_term_file = sys.argv[1]
replace_term_file = sys.argv[2]
target_file = sys.argv[3]
print([search_term_file, replace_term_file, target_file])
with open(search_term_file, 'r') as file:
search_term = file.read()
with open(replace_term_file, 'r') as file:
replace_term = file.read()
with open(target_file, 'r') as file:
content = file.read()
print([search_term, replace_term])
content = content.replace(search_term, replace_term)
with open(target_file, 'w') as file:
file.write(content)
здесь есть реализация fsed
с использованием grep
и dd
(via)
ограничение: шаблон может соответствовать только одним строкам = шаблон не может содержать \n
эта версия будет заменять только первое совпадение. чтобы заменить больше совпадений, удалите -m 1
и пройдите через все совпадения
fixedReplaceFirst(){ # также fsed (редактор фиксированных строк)
input="$1"
pattern="$2"
replace="$3"
match="$(echo "$input" | grep -b -m 1 -o -E "$pattern")"
offset1=$(echo "$match" | cut -d: -f1)
match="$(echo "$match" | cut -d: -f2-)"
matchLength=${#match}
offset2=$(expr $offset1 + $matchLength)
echo "$input" | dd bs=1 status=none count=$offset1
echo -n "$replace"
echo "$input" | dd bs=1 status=none skip=$offset2
}
read -d '' input <<EOF
#%replace_me
здесь,
#%replace_me
не заменяется
EOF
read -d '' replace <<EOF
a=1
b=2
EOF
fixedReplaceFirst "$input" "^#%replace_me$" "$replace"
существует rpl (python), но он требует обычного файла, так что его нельзя использовать для замены stdin на stdout
$ echo $'[(\n.*' >temp.txt
$ rpl $'[(\n.*' 'yep'
$ cat temp.txt
yep
существует замена (C, nixpkgs), но она не работает с новыми строками
$ replace-literal '[(.*' 'yep' <<< '[(.*'
yep
$ replace-literal $'[(\n.*' 'yep' <<< $'[(\n.*'
[(
.*
на случай, если вдруг это мне понадобиться снова в будущем
subject=" http://a\tbc/ef.png?&;wert="3$3*4%5"#eft def "
search="http://a\tbc/ef.png?&;wert="3$3*4%5"#eft"
replace="e&*$%\tf.png"
echo "$subject" | awk -v srch=$(printf "%s" "$search"| sed -e 's/\\/\\\\\\\\/g' -e 's/[?&*.$]/\\\\\0/g' ) -v replc=$(printf "%s" "$replace"| sed -e 's/\\/\\\\/g' -e 's/[?&]/\\\\\0/g' ) '{gsub(srch, replc, $0); print}' 2> /dev/null
srch и replc экранируются в подshell
не уверен, добавляет ли это действительно ценность, но это должно обойти в основном все специальные символы
!!! ОДИН-СТРОЧНИК !!!
Все строки, заключенные в кавычки Bash (одинарные кавычки (‘…’), двойные кавычки (“…”) или кавычки с долларом ($’…’)), не интерпретируются как регулярные выражения, а вместо этого воспринимаются буквально.
zResult=${zInput//"$zMatch"/"замена"}
@ссылка на комментарий jhnc.
Вышеизложенный однострочник намного лучше, чем следующий цикл, который я первоначально задумал, который также использует возможности кавычек bash. Этот пример работает, но, как и каждый другой ответ, остаётся лишь для сложной задачи:
zInput="строка .* для .* поиска"
zMatch=".*"
zResult=""
while :; do
[[ "$zInput" =~ (.*)("$zMatch")(.*) ]] || break
zResult="замена${BASH_REMATCH[3]}$zResult"
zInput="${BASH_REMATCH[1]}"
done
zResult="$zInput$zResult"
echo "$zResult"
Примечание: результат строится в обратном порядке, потому что самый левый .* жадный.
Существовало решение на ruby для замены текста в файлах полностью без использования регулярных выражений. Вот еще одно на python прямо в bash
PYTHON_CODE=$(cat <<PY_END
##################
import os
f_path = "./"
patch_pairs=[ ('AAA', 'BBB') ] # AAA -> BBB
patch_pairs=patch_pairs+[ ('"CCC"', '"DDD"') ] # "CCC" -> "DDD"
# и т.д...
patch_exts= [".py"]
for r, d, f in os.walk(f_path):
for file_name in f:
for ext in patch_exts:
if file_name.endswith(ext):
fpath = os.path.join(r, file_name)
fpath = os.path.abspath(fpath)
try:
anychange = 0
with open(fpath, "r+") as text_file:
texts = text_file.read()
for pp in patch_pairs:
if pp[0] in texts:
anychange = anychange+1
texts = texts.replace(pp[0], pp[1])
if anychange>0:
with open(fpath, "w") as text_file:
text_file.write(texts)
print("// патч",fpath)
except Exception as e:
print("// не удалось пропатчить файл, пропускаем", fpath, e)
##################
PY_END
)
python -c "$PYTHON_CODE"
Ответ или решение
Существует несколько способов замены текста в файлах без использования регулярных выражений в командной строке bash. Вот несколько подходов, которые помогут вам выполнить эту задачу.
1. Использование встроенной замены в Bash
Если вы работаете с переменными, Bash позволяет вам производить замену строк напрямую:
original="Это пример строки с текстом для замены"
search="текст для замены"
replace="замененный текст"
modified="${original/"$search"/"$replace"}"
Эта строка выполнит замену текста в переменной original
. Заметьте, что при использовании этого метода не требуется экранирование специальных символов.
2. Использование sed
Хотя sed
работает с регулярными выражениями, вы можете использовать его для замены точных строк, выбирая другой разделитель, чтобы избежать экранирования /
. Например:
sed -i 's|строка|новая строка|g' path/to/your/file.txt
В этом примере мы использовали |
вместо /
, что позволяет избежать необходимости экранирования символов, содержащихся в строках.
3. Использование perl
с экранированием
Если вы предпочитаете использовать perl
, можно воспользоваться специальными экранирующими последовательностями:
perl -pe 's/\Qстрока\E/новая строка/g' path/to/your/file.txt
\Q
и \E
позволят вам безопасно вставить текст, не беспокоясь о специальных символах.
4. Использование Python
Скрипт на Python — еще один способ, который позволяет легко обрабатывать замену текста:
# replace.py
import sys
search_term = sys.argv[1]
replace_term = sys.argv[2]
target_file = sys.argv[3]
with open(target_file, 'r') as file:
content = file.read()
content = content.replace(search_term, replace_term)
with open(target_file, 'w') as file:
file.write(content)
Запуск этого скрипта будет выглядеть следующим образом:
python replace.py "строка" "новая строка" "path/to/your/file.txt"
5. Использование ruby
В ruby
вы также можете легко производить замену строк с помощью метода gsub
:
ruby -p -i -e "gsub('строка', 'новая строка')" path/to/your/file.txt
Заключение
Каждый из вышеупомянутых подходов имеет свои преимущества в зависимости от контекста вашей работы и удобства использования. Bash встроенные функции подходят для простых случаев, в то время как более мощные инструменты, такие как perl
, python
или ruby
, могут быть полезны для более сложных сценариев. Выбор метода зависит от ваших предпочтений и требований задачи.