sed заменить списком исключений

/usr/bin/awkв Solaris сильно ограничен в поддержке различных функций. В частности, функция gsub()не реализована. Это объясняется в руководстве для awkна Solaris .

Для этого вы должны использовать/usr/xpg4/bin/awk(или nawk, "новый awk" ).

В вашем коде также не указан разделитель полей для awk, поэтому $3никогда ничего не будет содержать, если в ваших данных не окажется достаточно пробелов, чтобы случайно создать третье поле, разделенное пробелами. Точно так же вы не указываете разделитель выходных полей, поэтому любые измененные строки будут иметь свои поля, разделенные пробелами.

Вместо:

/usr/xpg4/bin/awk -F '|' 'BEGIN { OFS=FS } { gsub(",", "|", $3); print }' file

Чтобы всегда получать /usr/xpg4/bin/awkв качестве awkпо умолчанию в Solaris, используйте PATH="$(/usr/xpg4/bin/getconf PATH):$PATH"в файле запуска вашей оболочки.

2
19.08.2021, 21:02
4 ответа

Чтобы не перегружать регулярное выражение, поскольку список исключений может быть более 200, мы сначала генерируем код sed, используя файл списка исключений, и применяем этот сгенерированный код к входным данным.

GNU-сед

sed -e '
  1i\
s/dank/\\n/g
  h;s:[\&/]:\\&:g
  x;s/dank/\n/g
  s:[][^$\/.*]:\\&:g
  s/\n/\\n/g;G
  s:\n:/:;s:.*:s/&/g:
  $a\
s/\\n/MONK/g
' excludes.txt | sed -f - file

Выход:-

xdankine reMONKus
dankzwd
MONKe MONKbe
testMONK

Подтверждение концепции:-

  • Во-первых, превратите все данки в буквальные символы новой строки, чтобы гарантировать, что char не будет найден.
  • Затем измените строку в списке исключений, например nudankip, как указано ниже, а также для всех строк в списке исключений.
  • s/nu\nip/nudankip/g
  • Сложность связана с тем, что нам нужно избежать списка исключений для правой и левой сторон выражения sed s///.
1
20.08.2021, 08:31

Если у вас есть только одно вхождение dankв строке, как в вашем примере, вы можете использовать инвертированные адреса:

sed -E '/dankine|dankzwd|nudankip|dankphys|danko\.mod/!s/dank/monk/'

Если в строке может быть несколько вхождений, вы можете использовать символ, который не может быть частью файла, например. грамм. #, изменить все dankна #, вернуться к списку слов и изменить #на monkдля остальных:

sed 's/dank/#/g;s/#ine/dankine/g;s/#zwd/dankzwd/g;s/nu#ip/nudankip/g;s/#phys/dankphys/g;s/#o\.mod/danko.mod/g;s/#/monk/g'

(Если может встречаться любой символ, используйте вместо него новую строку)

Обновление :Новое требование для чтения списка исключений из файла

Запишите свой черный список в файлexclusion.listс символом новой строки в конце(скрипт будет использовать это, чтобы определить, где заканчивается первый файл):

sed -e '1,/^$/{H;d;}' -e 'G;s/\n/&&/;:loop' -e 's/\(.*da\)\(nk.*\)\(.*\n\1\2\n\)/\1#\2\3/;tloop' -e 's/\n.*//;s/dank/monk/g;s/da#nk/dank/g' exclusion.list file

или,как многострочный, возможно, легче читать

sed '1,/^$/{H;d;}
  G
  s/\n/&&/
  :loop
  s/\(.*da\)\(nk.*\)\(.*\n\1\2\n\)/\1#\2\3/
  tloop
  s/\n.*//
  s/dank/monk/g
  s/da#nk/dank/g' exclusion.list file

Во всяком случае, это может быть легче написать, чем прочитать. Концепция

  • Прочитайте список исключений, чтобы освободить место
  • Для каждой строки файла добавьте этот список в пространство хранения
  • Замените каждый dankв файле, который появляется в списке, на da#nk, чтобы предотвратить его замену позже
  • Затем удалите список, замените все dankна monkи, наконец, удалите #из da#nkс.

Добавление lпосле :loopиллюстрирует принцип работы.

Спасибо Стефану за намек на проблему dankfoodank, которая здесь решается. Однако требование для случая dankdankостается неясным. Должно ли это быть dankmonk, потому что только dankиз dankdaзащищено, или оно должно оставаться dankdank, потому что daвторого dankзащищено как часть dankdaили это выходит за рамки?

4
20.08.2021, 10:26

Использование любого awk в любой оболочке на каждом компьютере Unix и использование литеральных строковых операций, поэтому нам не нужны метасимволы регулярных выражений или обратных ссылок во входных данных или списке исключений:

$ cat tst.awk
NR==FNR {
    mask[$0] = RS NR RS
    next
}
{
    delete changed
    for (exception in mask) {
        while ( s=index($0,exception) ) {
            $0 = substr($0,1,s-1) mask[exception] substr($0,s+length(exception))
            changed[exception]
        }
    }

    gsub(/dank/,"monk")

    for (exception in changed) {
        while ( s=index($0,mask[exception]) ) {
            $0 = substr($0,1,s-1) exception substr($0,s+length(mask[exception]))
        }
    }

    print
}

$ awk -f tst.awk exceptions file
xdankine remonkus
dankzwd
monke monkbe
testmonk

Вышеизложенное предполагает, что у вас нет исключений, которые являются подстроками других исключений, таких как dankfooи dankdankfoo, поскольку вы не показываете такие случаи в примере в вашем вопросе. Если вы это сделаете, убедитесь, что файл исключений отсортирован таким образом, что более длинные суперстроки идут перед более короткими подстроками и повторяются в том порядке, в котором они были введены, чтобы вы не заменяли xdankdankfooyна xdank<replacement>yвместо x<replacement>y, когда маскирование исключений в первом цикле.

2
20.08.2021, 10:26

С помощью perlможно сделать:

perl -pe '
  BEGIN{
    chomp (@excl = <STDIN>);
    $re = "(". join( "|", map {qr{\Q$_\E}} @excl). ")|dank"
  }
  s{$re}{$1//"monk"}ge' input < exclusion.list

Это создает регулярное выражение, такое как:

(dankine|dankzwd|nudankip|dankphys|danko\.mod)|dank

И мы заменяем любое его вхождение либо на$1(так что было сопоставлено, то есть по сути ничего не делая )если $1установлено (одно из исключений совпало )или monkв противном случае(dankвместо ).

Обратите внимание, что если исключения включают как dankzwd, так и zwddank, это все равно превратит dankzwddankв dankzwdmonk, поскольку сначала заменяет dankzwdна dankzwd($1), а затем остается только dankдля его заменить.

Подход к адресации, заключающийся в том, чтобы записывать все места, где происходит какое-либо исключение в строке маски, а затем, когда дело доходит до замены dank, выполнять замену только там, где маска говорит, что все в порядке.

perl -spe '
  BEGIN {
    chomp (@excl = <STDIN>);
    $word_len = length $word;
  }
  my $len = length;
  my $mask = "-" x $len;
  my $i;
  for my $e (@excl) {
    my $e_len = length $e;
    my $hide = "#" x $e_len;
    for (my $o = 0;
         $o < $len && ($i = index($_, $e, $o)) >= 0;
         $o = $i + 1) {
      substr($mask, $i, $e_len) = $hide;
    }
  }
  s{dank}{substr($mask, pos, $word_len) =~ /-/ ? $repl : $&}ge
  ' -- -word=dank -repl=monk input < exclusion.list

Так, например, если входная строка содержит:

dodankdankdankoodankdodank

и исключения имеют :dankdank, dankdoмаска будет построена постепенно из

 --------------------------
 --########---------------- # first dankdank
 --############------------ # second dankdank
 --############--######---- # first and only dankdo
 dodankdankdankoodankdodank
                       ^^^^

Тогда остается только dankв конце, на который не влияют исключения.

1
20.08.2021, 10:26

Теги

Похожие вопросы