Ответ 1
Возможно, что-то вроде этого:
sed 's/ab/~~/g; s/bc/ab/g; s/~~/bc/g'
Замените ~
символом, который, как вы знаете, не будет в строке.
Предположим, что у меня строка abbc, и я хочу заменить:
Если я попробую заменить два, результат не то, что я хочу:
echo 'abbc' | sed 's/ab/bc/g;s/bc/ab/g'
abab
Итак, какую команду семени можно использовать для замены, как показано ниже?
echo abbc | sed SED_COMMAND
bcab
ИЗМЕНИТЬ:
На самом деле текст может содержать более двух шаблонов, и я не знаю, сколько замен будет мне нужно. Поскольку был ответ, говорящий, что sed
является редактором потока, и его замены жадно, я думаю, что мне нужно будет использовать для этого какой-то язык script.
Возможно, что-то вроде этого:
sed 's/ab/~~/g; s/bc/ab/g; s/~~/bc/g'
Замените ~
символом, который, как вы знаете, не будет в строке.
Это может сработать для вас (GNU sed):
sed -r '1{x;s/^/:abbc:bcab/;x};G;s/^/\n/;:a;/\n\n/{P;d};s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/;ta;s/\n(.)/\1\n/;ta' file
Это использует таблицу поиска, которая подготовлена и удерживается в пространстве удержания (HS), а затем добавляется к каждой строке. Уникальный маркер (в данном случае \n
) добавляется к началу строки и используется как метод для поиска по всей длине строки. Как только маркер достигает конца строки, процесс завершается и распечатывается таблица поиска и отбрасываются маркеры.
N.B. Таблица поиска подготовлена в самом начале и второй уникальный маркер (в данном случае :
), выбранный так, чтобы не столкнуться с строками подстановки.
С комментариями:
sed -r '
# initialize hold with :abbc:bcab
1 {
x
s/^/:abbc:bcab/
x
}
G # append hold to patt (after a \n)
s/^/\n/ # prepend a \n
:a
/\n\n/ {
P # print patt up to first \n
d # delete patt & start next cycle
}
s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/
ta # goto a if sub occurred
s/\n(.)/\1\n/ # move one char past the first \n
ta # goto a if sub occurred
'
Таблица работает следующим образом:
** ** replacement
:abbc:bcab
** ** pattern
Вот вариант ooga answer, который работает для поиска и замены пар без необходимости проверять, как значения могут быть повторно использованы:
sed -i '
s/\bAB\b/________BC________/g
s/\bBC\b/________CD________/g
s/________//g
' path_to_your_files/*.txt
Вот пример:
перед:
some text AB some more text "BC" and more text.
после
some text BC some more text "CD" and more text.
Обратите внимание, что \b
обозначает границы слов, что мешает ________
мешать поиску (я использую GNU sed 4.2.2 на Ubuntu). Если вы не используете поиск по границе слова, тогда эта техника может не работать.
Также обратите внимание, что это дает те же результаты, что и удаление s/________//g
и добавление && sed -i 's/________//g' path_to_your_files/*.txt
до конца команды, но не требует указания пути дважды.
Общая вариация на это заключалась бы в использовании \x0
или _\x0_
вместо ________
, если вы знаете, что в ваших файлах нет нулей, в качестве jthill предложил.
sed
- редактор потока. Он ищет и с жадностью заменяет. Единственный способ сделать то, что вы просили, - использовать промежуточный шаблон замещения и изменить его в конце.
echo 'abcd' | sed -e 's/ab/xy/;s/cd/ab/;s/xy/cd/'
Tcl имеет встроенный для этого
$ tclsh
% string map {ab bc bc ab} abbc
bcab
Это работает, перемещая строку символа за раз, выполняя строковые сравнения, начиная с текущей позиции.
В perl:
perl -E '
sub string_map {
my ($str, %map) = @_;
my $i = 0;
while ($i < length $str) {
KEYS:
for my $key (keys %map) {
if (substr($str, $i, length $key) eq $key) {
substr($str, $i, length $key) = $map{$key};
$i += length($map{$key}) - 1;
last KEYS;
}
}
$i++;
}
return $str;
}
say string_map("abbc", "ab"=>"bc", "bc"=>"ab");
'
bcab
Вот awk
на основе oogas sed
echo 'abbc' | awk '{gsub(/ab/,"xy");gsub(/bc/,"ab");gsub(/xy/,"bc")}1'
bcab