Вопрос или проблема
Как мы все знаем, sed
очень эффективен для поиска и замены строк, например, найти ‘a’ и заменить на ‘b’: sed 's/a/b/g'
.
Можно ли сделать это с помощью других команд или скриптов оболочки вместо sed
?
Это для ограниченных систем Linux для телевизоров, на которых отсутствует команда sed
. Поэтому мне нужно использовать другие команды или скрипты вместо sed 's/a/b/g'.
Классической альтернативой для замены одиночных букв является команда tr
, которая должна быть доступна на любой системе:
$ echo "foobar" | tr a b
foobbr
tr
на самом деле лучше, чем sed
, для этой задачи, поскольку использование sed
(не говоря уже о perl
или awk
) для замены одиночных букв похоже на то, как использовать кувалду для того, чтобы убить муху.
grep
не предназначен для этого, он не изменяет свой вход, а лишь ищет в нем.
В качестве альтернативы, вы можете использовать возможности замены некоторых оболочек. Вот как это выглядит с использованием синтаксиса ksh
, zsh
или bash
:
$ foo="foobar"
$ echo "${foo//a/b}"
foobbr
Мы могли бы дать вам более конкретные ответы, если бы вы объяснили, какую именно проблему вы пытаетесь решить.
Да, есть множество способов сделать это. Вы также можете использовать awk
, perl
или bash
для выполнения этих действий. В общем, однако, sed
вероятно является наиболее подходящим инструментом для выполнения таких задач.
Примеры
Скажем, у меня есть эти образцы данных в файле data.txt
:
foo bar 12,300.50
foo bar 2,300.50
abc xyz 1,22,300.50
awk
$ awk '{gsub("foo", "foofoofoo", $0); print}' data.txt
foofoofoo bar 12,300.50
foofoofoo bar 2,300.50
abc xyz 1,22,300.50
Perl
$ perl -pe "s/foo/foofoofoo/g" data.txt
foofoofoo bar 12,300.50
foofoofoo bar 2,300.50
abc xyz 1,22,300.50
Редактирование на месте
Приведенные выше примеры также могут напрямую изменять файлы. Пример с Perl тривиален. Просто добавьте переключатель -i
.
$ perl -pi -e "s/foo/foofoofoo/g" data.txt
Для awk
это немного менее прямолинейно, но столь же эффективно:
$ { rm data.txt && awk '{gsub("foo", "foofoofoo", $0); print}' > data.txt; } < data.txt
Этот метод создает подпроцесс с фигурными скобками ‘{ … }`, где файл перенаправляется в него следующим образом:
$ { ... } < data.txt
После того как файл был перенаправлен в подпроцесс, он удаляется, а затем awk
выполняется над содержимым файла, которое было прочитано в STDIN подпроцесса. Это содержимое затем обрабатывается awk
и записывается обратно в тот же файл с именем, который мы только что удалили, тем самым заменяя его.
Вы можете использовать инструмент POSIX ex
:
ex a.txt <<eof
%s/Sunday/Monday/g
xit
eof
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ex.html
Если вы работаете с файлом, а не с потоком, вы можете использовать стандартный текстовый редактор ed
:
printf '%s\n' ',s/a/b/g' w q | ed file.txt
Это должно быть доступно на любом *nix. Запятая в ',s/a/b/g'
указывает ed
работать на каждой строке (вы также можете использовать %
, что будет более привычно, если вы привыкли к vim), а остальная часть — это стандартный поиск и замена. w
говорит ему записать (сохранить) файл, q
говорит ему выйти.
Обратите внимание, что в отличие от параметра -i
в sed (и аналогичных параметров в других инструментах), это действительно редактирует файл на месте, а не использует временные файлы.
Я не думаю, что это возможно сделать с потоками, но я также не знаю много о ed
, и меня не удивит, если у него действительно есть такая возможность (походя на философию Unix).
Используя команду предка ed (где -s
означает тихий (беззвучный), а запятая перед командами означает выполнение на всех строках):
Просто печатаем на STDOUT:
ed -s file <<!
,s/a/b/g
,p
q
!
замена на месте:
ed -s file <<!
,s/a/b/g
w
q
!
Если вы используете операционную систему Linux, например, Ubuntu Desktop, как я, у вас будет установлен python
.
С помощью Python мы можем выполнять замену текста следующим образом:
result=$(python << EOT
s="$your_string_in_shell_script"
r = s.replace('text to replace', 'replaced by this text')
print(r)
EOT)
echo $result
Если у вас есть Python, вы можете проходить по дереву папок и заменять что угодно, не углубляясь в регулярные выражения. Найти и заменить на месте:
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"
Ответ или решение
Вопрос, как найти и заменить строку в текстовых файлах без использования команды sed
, вполне актуален, особенно в средах, где этот инструмент не доступен. К счастью, существует множество альтернативных способов, которые могут эффективно решить данную задачу. Рассмотрим несколько из них.
1. Использование команды tr
Если необходимо заменить одиночные символы, можно воспользоваться командой tr
, которая доступна практически на всех операционных системах Linux:
echo "foobar" | tr 'a' 'b'
# Вывод: foobbr
2. Подстановка в оболочке (bash, ksh, zsh)
Современные оболочки, такие как bash
, поддерживают встроенные возможности подстановки, что позволяет делать замену напрямую в переменной:
foo="foobar"
echo "${foo//a/b}"
# Вывод: foobbr
3. Использование awk
Для более сложных замен в текстовых файлах можно использовать awk
, который отлично подходит для обработки текстовых потоков:
awk '{gsub("foo", "foofoofoo", $0); print}' data.txt
Этот пример заменяет все вхождения foo
на foofoofoo
в файле data.txt
.
4. Использование perl
Perl
также является мощным инструментом для выполнения замен:
perl -pe "s/foo/foofoofoo/g" data.txt
Для редактирования файла на месте, добавьте опцию -i
:
perl -pi -e "s/foo/foofoofoo/g" data.txt
5. Использование текстового редактора ed
Если вы работаете с файлами, можно применить традиционный текстовый редактор ed
:
printf '%s\n' ',s/a/b/g' w q | ed file.txt
Здесь запятая означает, что замена будет выполнена на каждой строке файла. Команды w
и q
сохраняют изменения и выходят из редактора, соответственно.
6. Использование ex
Также можно воспользоваться текстовым редактором ex
, чтобы сделать замену:
ex data.txt <<eof
%s/Sunday/Monday/g
xit
eof
7. Python
Если на системе установлен Python, можно выполнить замену текста с помощью следующего кода:
result=$(python <<EOT
s = "$your_string_in_shell_script"
r = s.replace('text to replace', 'replaced by this text')
print(r)
EOT)
echo $result
Для включения замены в нескольких файлах в директории, можно использовать следующий скрипт Python:
import os
patch_pairs = [('AAA', 'BBB'), ('"CCC"', '"DDD"')] # Пары для замены
patch_exts = ['.py'] # Принимаемые расширения файлов
for r, d, f in os.walk('./'):
for file_name in f:
for ext in patch_exts:
if file_name.endswith(ext):
fpath = os.path.join(r, file_name)
with open(fpath, "r") as text_file:
texts = text_file.read()
for old, new in patch_pairs:
texts = texts.replace(old, new)
with open(fpath, "w") as text_file:
text_file.write(texts)
Заключение
Существует множество альтернатив sed
, которые могут быть использованны в зависимости от задач и доступных инструментов. Выбор подходящего метода зависит от ваших потребностей и технических условий. Убедитесь, что выбранный вами инструмент удовлетворяет требованиям вашей среды разработки и доступности.