Як замінити відразу кілька шаблонів на sed?


231

Припустимо, у мене є рядок 'abbc' і я хочу замінити:

  • ab -> до н.е.
  • bc -> ab

Якщо я спробую дві заміни, результат є не тим, що я хочу:

echo 'abbc' | sed 's/ab/bc/g;s/bc/ab/g'
abab

Отже, яку команду sed я можу використовувати для заміни, як нижче?

echo abbc | sed SED_COMMAND
bcab

EDIT : Насправді текст може мати більше двох шаблонів, і я не знаю, скільки замінників мені знадобиться. Оскільки у відповіді сказано, що sedце редактор потоків, і його заміни жадібно, я думаю, що для цього мені потрібно буде використовувати мову скриптів.


Чи потрібно робити кілька замін на одній лінії? Якщо не просто скинути gпрапор з обох цих s///команд, і це спрацює.
Ітан Рейснер

Ви пропустили пункт мого запитання. Я мав на увазі, чи потрібно вам робити кожну заміну більше одного разу на одній лінії. Чи є декілька збігів для ab або bc в оригінальному введенні.
Ітан Рейснер,

Вибачте @EtanReisner, що я зрозумів неправильно. текст може мати кілька замін.
DaniloNC

Відповіді:


342

Можливо, щось подібне:

sed 's/ab/~~/g; s/bc/ab/g; s/~~/bc/g'

Замініть ~символом, який, на вашу думку, не буде в рядку.


9
GNU sed обробляє нулі, тому ви можете використовувати \x0для ~~.
jthill

3
Чи gпотрібно і що це робить?
Лі

12
@Lee gпризначений для глобального - він замінює всі екземпляри шаблону в кожному рядку, а не лише перший (що є поведінкою за замовчуванням).
naught101

1
Будь ласка , дивіться мій відповідь stackoverflow.com/a/41273117/539149 для зміни відповіді Ooga, що може замінити кілька комбінацій одночасно.
Зак Морріс

3
що ви знаєте, що не буде в рядку. Для коду виробництва не робіть жодного припущення щодо вводу. Щодо тестів, ну, тести дійсно ніколи не підтверджують правильність, але хороша ідея для тесту: Використовуйте сам сценарій як вхідний.
hagello

33

Я завжди використовую кілька заяв із "-e"

$ sed -e 's:AND:\n&:g' -e 's:GROUP BY:\n&:g' -e 's:UNION:\n&:g' -e 's:FROM:\n&:g' file > readable.sql

Це додасть '\ n' перед усіма AND, GROUP BY, UNION та FROM, тоді як '&' означає відповідна рядок, а '\ n &' означає, що ви хочете замінити відповідна рядок на '\ n' перед ' '


14

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

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 's/ab/xy/' | sed 's/cd/ab/' .....)
leetbacoon

12

sedє редактором потоку. Він жадібно шукає та замінює. Єдиний спосіб зробити те, про що ви просили, - це використовувати проміжну схему заміни та змінити її в кінці кінців.

echo 'abcd' | sed -e 's/ab/xy/;s/cd/ab/;s/xy/cd/'


4

Це може допомогти вам (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) готується до початку рядка і використовується як метод для підбиття пошуку по всій довжині рядка. Як тільки маркер доходить до кінця рядка, процес закінчується і роздруковується таблиця пошуку та маркери, які відкидаються.

Примітка: Таблиця пошуку підготовлена ​​на самому початку та :вибраний другий унікальний маркер (у цьому випадку ), щоб не зіткнутися з рядками заміни.

З деякими коментарями:

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

3

Можливо, буде спрощений підхід до появи одного шаблону, який ви можете спробувати нижче: echo 'abbc' | sed 's / ab / bc /; s / bc / ab / 2'

Мій вихід:

 ~# echo 'abbc' | sed 's/ab/bc/;s/bc/ab/2'
 bcab

Для кількох випадків дії шаблону:

sed 's/\(ab\)\(bc\)/\2\1/g'

Приклад

~# cat try.txt
abbc abbc abbc
bcab abbc bcab
abbc abbc bcab

~# sed 's/\(ab\)\(bc\)/\2\1/g' try.txt
bcab bcab bcab
bcab bcab bcab
bcab bcab bcab

Сподіваюся, це допомагає !!


2

Tcl має вбудований для цього

$ tclsh
% string map {ab bc bc ab} abbc
bcab

Це працює, якщо ходити рядок символу за раз, роблячи порівняння рядків, починаючи з поточної позиції.

У перл:

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

0

Ось на awkоснові огазsed

echo 'abbc' | awk '{gsub(/ab/,"xy");gsub(/bc/,"ab");gsub(/xy/,"bc")}1'
bcab
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.