Как я могу использовать awk, чтобы вставить что-то в середину слова?

avatar
ans
8 апреля 2018 в 09:14
224
3
1

У меня есть ввод:

This is a test

И я хочу вставить несколько букв в середине слова, например:

This is a teSOMETHINGst

Я знаю, что могу определить нужное слово с помощью $i, но как я могу изменить слово таким образом?

Я пытаюсь сделать это так:

{
    i=4 # finding somehow
    print (substr($i,1,(length($i)/2)) "SOMETHING" substr($i,(length($i)/2),(length($i)/2)))
}

Поскольку я новичок в awk, мне интересно, правильный ли это путь.

Источник

Ответы (3)

avatar
Inian
8 апреля 2018 в 14:31
2

Предполагая, что ваше требование состоит в том, чтобы сопоставить номер столбца, содержащий test, и выполнить над ним некоторые операции, выполнить простой цикл по столбцам до NF и сопоставить с помощью оператора сопоставления регулярного выражения ~ или для фиксированных строк выполнить равенство соответствует как $i == "test"

awk '
{
  for(i=1;i<=NF;i++) {
    if ($i ~ "test") {
      halfLength=(length($i)/2)
      $i=(substr($i,1,halfLength) "SOMETHING" substr($i,(halfLength+1),halfLength))
    }
  }
}1' <<<"This is a test"

Выдает ожидаемый результат. Обратите внимание, что я сделал вызов substr() для печати второй части строки как substr($i,(halfLength+1),halfLength). Требуется +1, который вы пропустили раньше. Я использовал результат substr() для изменения номера столбца, содержащего test, то есть как $i=..

.

Кроме того, при выполнении {..}1 каждое из полей столбца реконструируется на основе изменений, если таковые имеются, в нашем случае только для столбца, содержащего нужную строку.

Также обратите внимание, что вся попытка завершится неудачей, если целевая строка содержит нечетное количество символов или образует подстроку другой строки большего размера (можно использовать оператор равенства, но подход с регулярным выражением не удастся)

Ed Morton
8 апреля 2018 в 17:19
0

это не удастся, если целевая строка состоит из нечетного числа символов (например, tests) и когда целевое слово является частью другого слова (например, contestant).

Inian
8 апреля 2018 в 17:23
1

@EdMorton: Конечно, Эд! Знал это, просто хотел исправить попытку ОП. Добавлю примечание о случаях, которые вы упомянули.

Inian
8 апреля 2018 в 17:29
1

@EdMorton: Готово, Эд! Теперь они перескочат прямо к вам, увидев мой ответ;)

avatar
James Brown
8 апреля 2018 в 17:44
1

Еще один, который вырос из любопытства в личную месть (:

$ echo This is a contestant test | 
awk -v s="test" '
BEGIN {
    FS=OFS=""
}
{
    if(i=match($0, "(^| )" s "( |$)")) {   # match over index since regex support
        j=(i+length(s)/2+!!(i-1))          # !!(i-1) detect beginning of record
        $j="SOMETHING" $j
    }
}1'
This is a contestant teSOMETHINGst

Другой использует пустые разделители, в основном для удовлетворения личного любопытства:

$ echo This is a test | 
awk -v s="test" '
BEGIN {
    FS=OFS=""                # empty separators
}
{
    if(i=index($0,s)) {      # index finds the beginning of test
        j=(i+length(s)/2)    # midpoint
        $j="SOMETHING" $j    # insert string
    }
}1'                          # output
This is a teSOMETHINGst

Ed Morton
8 апреля 2018 в 17:54
0

это неправильно найдет test, если оно находится в пределах contestant. Он также полагается на неопределенное поведение (то, что awk делает с FS="", не указано в POSIX), поэтому YMMV.

James Brown
8 апреля 2018 в 17:56
0

Конечно будет, совсем не стандартное доказательство. Мне просто было интересно проверить схему. Также удивлен, что он работал с mawk и original-awk.

Ed Morton
8 апреля 2018 в 17:57
0

рассмотрите возможность добавления теста для символа до и после целевой строки, не являющейся альфа-или-^ и неальфа-или-$ соответственно,

James Brown
8 апреля 2018 в 17:59
0

Я играл с проверкой границ слов, но мне стало скучно. :D Ленивое воскресенье.

Ed Morton
8 апреля 2018 в 18:07
1

Да, очень жаль, что нет способа сказать «искать эту строку не в другой строке», но на этом пути лежит ужасный взрыв синтаксиса p###, поэтому вместо этого мы должны написать что-то вроде i=index($0,s) && ((i==1) || (substr($0,i-1,1) !~ /[[:alpha:]]/) && (((i+length(s))==length($0)) || (substr($0,i+length(s),1) !~ /[[:alpha:]]/))

James Brown
8 апреля 2018 в 18:57
1

Кроме того, произойдет сбой, если contestant было до test, так как нет цикла для прохождения первого попадания. Обречен с самого начала. Сколько способов человек может потерпеть неудачу? (Добавьте в в начало этого предложения и принесите соль)

Ed Morton
8 апреля 2018 в 20:27
0

Почему !! в !!(i-1)?

James Brown
8 апреля 2018 в 20:30
0

Если i==1 !!(i-1)==0, но, например, i==23 -> !!(i-1)==1, из-за смещения (^| ). Почти полночь, завтра мне будет стыдно...

Ed Morton
8 апреля 2018 в 20:32
1

Так что это вместо (i>1) - хорошо, понял, спасибо за объяснение.

avatar
Ed Morton
8 апреля 2018 в 17:07
3

Это может быть то, что вы ищете:

$ awk 'match($0,/\<test\>/){mid=int(RLENGTH/2); $0=substr($0,RSTART,mid) "SOMETHING" substr($0,RSTART+mid,RELNGTH-mid)} 1'

например. некоторые тестовые примеры (не каламбур):

$ echo 'This is a test' |
awk 'match($0,/\<test\>/){mid=int(RLENGTH/2); $0=substr($0,RSTART,mid) "SOMETHING" substr($0,RSTART+mid,RLENGTH-mid)} 1'
teSOMETHINGst

$ echo 'These are tests' |
awk 'match($0,/\<tests\>/){mid=int(RLENGTH/2); $0=substr($0,RSTART,mid) "SOMETHING" substr($0,RSTART+mid,RLENGTH-mid)} 1'
teSOMETHINGsts

$ echo 'These contestants are in a test' |
awk 'match($0,/\<test\>/){mid=int(RLENGTH/2); $0=substr($0,RSTART,mid) "SOMETHING" substr($0,RSTART+mid,RLENGTH-mid)} 1'
teSOMETHINGst