САС заменяет несколько первых вхождений (и диапазонов) паттерна - PullRequest
2 голосов
/ 17 июня 2019

это возможно, чтобы изменить первые 4 (или более) вхождения строки в этом сценарии, используя SED (в противоположность sed -r 's/[^[:space:]]*/TEST/4g'):

TEST TEST TEST TEST five six seven

Я получаю это работает с обратным порядком слов в строке, используяДважды AWK, но это долго, сложно, и я хочу получить это только с помощью SED:

echo one two three four five six seven | awk '{for(i=NF;i>=1;i--) printf "%s ", $i;print ""}'  | sed -r 's/[^ ]*/TEST/4g' |  awk '{for(i=NF;i>=1;i--) printf "%s ", $i;print ""}'

Также, возможно, есть возможность изменить диапазоны вхождения, как 3-5, 6-12, ...?

Пример ввода:

один два три четыре пять шесть семь

восемь девять десять одиннадцать двенадцать тринадцать четырнадцать

пятнадцать шестнадцать семнадцать восемнадцатьдевятнадцать двадцать двадцать один

Ответы [ 4 ]

3 голосов
/ 17 июня 2019

А как насчет AWK :

awk '{for(i=1;i<=NF;i++) if(i<5){$i="TEST"}; print}'

Тестовый прогон:

$ echo one two three four five six seven | awk '{for(i=1;i<=NF;i++) if(i<5){$i="TEST"}; print}'
TEST TEST TEST TEST five six seven

Это решение короткое, удобочитаемое и обслуживаемое. Если вас это не устраивает, пожалуйста, добавьте некоторые подробности о вашей конкретной проблеме.


Perl эквивалентное решение:

perl -pe 's/\S+/$i++<4?"TEST":$&/ge'

Тестовый прогон:

$ echo one two three four five six seven | perl -pe 's/\S+/$i++<4?"TEST":$&/ge'
TEST TEST TEST TEST five six seven

возможно, есть опция для изменения диапазонов, например 3-5, 6-12

AWK:

awk '{for(i=3;i<6;i++)$i="TEST";print}'

Тестовый запуск для вновь предоставленного входного файла:

$ awk '{for(i=3;i<6;i++)$i="TEST";print}' input
one two TEST TEST TEST six seven
eight nine TEST TEST TEST thirteen fourteen
fifteen sixteen TEST TEST TEST twenty twenty-one

Perl:

perl -pe 's/\S+/++$c~~[3..5]?"TEST":$&/ge'

Тестовый запуск для вновь предоставленного входного файла:

$ perl -pe '$c=0;s/\S+/++$c~~[3..5]?"TEST":$&/ge' input
Smartmatch is experimental at -e line 1. <== This is a warning that goes to STDERR
one two TEST TEST TEST six seven
eight nine TEST TEST TEST thirteen fourteen
fifteen sixteen TEST TEST TEST twenty twenty-one
1 голос
/ 17 июня 2019

Ответ был предоставлен здесь mikeserv . ПРИМЕЧАНИЕ : если вы хотите обработать диапазон, вам нужно использовать максимальную границу, поскольку она будет обрабатывать столько совпадений, сколько возможно, без каких-либо исключений / ошибок.

GNU sed:

echo 'one two three four five six seven' | \
  sed 's/[^[:space:]]*/\n&/g;:t;/\n/{x;/.\{4\}/!{s/$/./;x;s/\n[^[:space:]]*/TEST/;bt};x};s/\n//g'

POSIX sed:

nl='
';
echo 'one two three four five six seven' | sed "s/[^[:space:]]*/\\$nl&/g;:t${nl}/\n/{x;/.\{4\}/!{${nl}s/$/./;x;s/\n[^[:space:]]*/TEST/;bt$nl};x$nl};s/\n//g"

См. online sed demo .

Исходное объяснение (обратите внимание, что здесь, 1 заменен на 2, вы можете использовать любые другие шаблоны):

Там я использую две известные техники.Во-первых, каждое вхождение 1 в строке заменяется на \n1.Таким образом, поскольку я выполняю рекурсивные замены далее, я могу быть уверен, что не заменит вхождение дважды , если моя строка замены содержит мою строку замены.Например, если я заменю he на hey, он все равно будет работать.

Я делаю это так:

s/1/\
&/g

Во-вторых, я рассчитываю замены, добавляя символ вh старое место для каждого вхождения.Как только я достигну трех, больше не произойдет.Если вы примените это к своим данным и измените \{3\} на общее количество замен, которое вы желаете, а /\n1/ адреса на то, что вы хотите заменить, вы должны заменить только столько, сколько пожелаете.

0 голосов
/ 17 июня 2019
$ echo "one two three four fix six" | \
sed -E ':r s/(^|(TEST )+)[^ ]*/\1TEST/;/^(TEST ){4}/!br'
TEST TEST TEST TEST fix six

Объяснение:

  • :r метка с именем r переходит обратно на
  • s/(^|(TEST )+)[^ ]*/\1TEST/; замена, которая заменяет только один вхождениене-TEST слово, которому предшествует либо начало строки, либо 1 или более TEST
  • /^(TEST ){4}/!br' регулярное выражение для того, что нужно, за которым следует !br для перехода обратно к :r, если оно еще не найдено.

Очевидно, что это хрупкий.Он будет зацикливаться бесконечно, если в одной строке нет четырех слов.Может быть только GNU sed.

0 голосов
/ 17 июня 2019

Это совершенно неуместная задача для sed, так как sed предназначен для выполнения простых s/old/new/ для отдельных строк, , то есть всего .С любым awk в любой оболочке на каждом компьютере UNIX:

$ echo one two three four five six seven | awk '{for (i=1; i<=4; i++) $i="TEST"}1'
TEST TEST TEST TEST five six seven

$ echo one two three four five six seven | awk '{for (i=3; i<=5; i++) $i="TEST"}1'
one two TEST TEST TEST six seven

и, если вам нужно его параметризовать:

echo one two three four five six seven |
    awk -v beg=3 -v end=5 '{for (i=beg; i<=end; i++) $i="TEST"}1'
one two TEST TEST TEST six seven
...