하위 문자열만 변경

하위 문자열만 변경

START패턴으로 표시된 섹션 앞뒤에 잘못된 텍스트가 있는 파일 END(각 특정 문자열은 한 번만 나타나며 같은 줄에 올바른 순서로 나타납니다). 나는 단지 START와 사이의 부분에 대해 문자열 조작을 하고 싶습니다 .END

입력 예:

aomodi3hriq32| ¶³r 0q93aoiSTART_this_is_to_be_modified_ENDaqsdofuha23uru| ²23i ii3uhfia
oawpo3<9"§ A hSTART_this_also_needs_modification_ENDqa 032/a237(°1Q"§ >A_this_
START changeme ENDnot_this_modias

- 연산에 관한 한, sed및 사이의 하위 문자열(및 하위 문자열만)은 내가 사용하는 것처럼 수정되어야 합니다.STARTENDsed 's/_this_// ; s/modi/MODI/ ; y/as/45/'

출력 예:

aomodi3hriq32| ¶³r 0q93aoiSTARTi5_to_be_MODIfied_ENDaqsdofuha23uru| ²23i ii3uhfia
oawpo3<9"§ A hSTART4l5o_need5_MODIfic4tion_ENDqa 032/a237(°1Q"§ >A_this_
START ch4ngeme ENDnot_this_modias

awk여러 값을 다른 위치에 설정할 수 없기 FS="START|END"때문에 실패합니다 .OFS

중첩된 명령 대체 및 다른 구분 기호( ) 를 사용해 보았 으나 실패했으며, 명령 앞/뒤에 문자가 있어 명령을 엉망으로 만들 수 있다는 점도 sed우려했습니다 (예: a ). ~아이디어는 "내부" 하위 문자열만 선택하고 작업을 수행한 다음 이를 대체의 일부로 사용하는 것입니다.STARTEND/

sed "s/^\(.*\)START.*END\(.*\)$/\1$(sed 's~^.*START~~
                                         s~END.*~~
                                         s~_this_~~
                                         s~modi~MODI~
                                         y~as~45~' infile)\2/" infile

나는 예를 들어 perl....하지만 무엇이든 익숙하지 않습니다.

sedREGEX 일치 하위 문자열 행에만 집합 작업을 적용할 수 있는 방법이 있습니까 ?

답변1

perl -CSD -ne '
    if (my ($before, $between, $after) = /^(.*START)(.*)(END.*)/) {
        s/_this_//, s/modi/MODI/, tr/as/45/ for $between;
        print "$before$between$after\n";
    } else { print; }' -- file
  • -CSDUTF-8에서 입력을 디코딩하고 출력을 UTF-8로 인코딩합니다.
  • $before대신 및 를 사용하여 세 가지 변수 , 및 를 채울 수 있지만 $between더 나은 솔루션을 찾지 못했습니다. $after/p${^PREMATCH}${^POSTMATCH}
    if (my ($s) = /START(.*)END/p) {
        s/_this_//, s/modi/MODI/, tr/as/45/ for $s;
        print "${^PREMATCH}START${s}END${^POSTMATCH}";
    } else { print; }
    

START...END 부분이 한 줄에서 반복될 수 있는 경우 각 줄을 반복해야 합니다.

for my $part (split /(START.*?END)/) {
    if ($part =~ /^START.*END$/) {
        s/_this_//, s/modi/MODI/, tr/as/45/ for $part;
    }
    print "$part";
}

답변2

기준을 사용 sed하고 각 줄에 정확히 하나 START와 하나의 END하위 문자열이 포함되어 있다고 가정합니다(순서대로).

# Skip (pass through) lines that does not have START followed by END.
/.*START\(.*\)END.*/ !b

# Save the original line in the hold space.
h

# Remove the start and the end from the line.
# This leaves the bit of the line that we want to modify.
# (This reuses the previous regular expression.)
s//\1/

# Modify what's left.
s/_this_//
s/modi/MODI/
y/as/45/

# Append the original line from the hold space,
# with a newline as delimiter.
G

# Move the modified bit into the correct spot with a substitution,
# while deleting the old substring between START and END.
s/\(.*\)\n\(.*START\).*\(END.*\)/\2\1\3/

시험:

$ cat file
aomodi3hriq32| ¶³r 0q93aoiSTART_this_is_to_be_modified_ENDaqsdofuha23uru| ²23i ii3uhfia
oawpo3<9"§ A hSTART_this_also_needs_modification_ENDqa 032/a237(°1Q"§ >A_this_
START changeme ENDnot_this_modias
$ sed -f script file
aomodi3hriq32| ¶³r 0q93aoiSTARTi5_to_be_MODIfied_ENDaqsdofuha23uru| ²23i ii3uhfia
oawpo3<9"§ A hSTART4l5o_need5_MODIfic4tion_ENDqa 032/a237(°1Q"§ >A_this_
START ch4ngeme ENDnot_this_modias

인라인, 명령줄에서:

sed -e '/.*START\(.*\)END.*/!b' -e h -e 's//\1/' \
    -e 's/_this_//' -e 's/modi/MODI/' -e 'y/as/45/' \
    -e G -e 's/\(.*\)\n\(.*START\).*\(END.*\)/\2\1\3/' file

답변3

언제든지 자신만의 OFS를 여러 개 구축할 수 있습니다.

awk -v FS='START|END' -v OFS= -v map='_this_\r\rmodi\rMODI\ra\r4\rs\r5' '
  BEGIN{ split(FS, mOFS, "|") }
  { n=split(map, tr, "\r"); for(i=1; i<n; i+=2) gsub(tr[i], tr[i+1], $2);
  print $1, mOFS[1], $2, mOFS[2], $3
}' infile

gsub()의 첫 번째 인수는 정규식이므로 정의할 때 주의하세요.map=....;오른쪽 매핑에는 &역참조 \1등과 같은 일부 특수 문자가 있어서는 안 됩니다. 그러나 매핑을 수동으로 작성할 때 특수 문자를 이스케이프하여 gsub()에 의해 독점적으로 해석되는 것을 방지할 수 있습니다.

CR을 사용하여 \r지도를 분리하고 있습니다. 말씀하신 대로 입력 파일에 존재하지 않는 유일한 것입니다. 단, \0Split() 및 awk의 다른 함수(또는 다른 프로그래밍에서는 작동하지 않습니다) 언어도 마찬가지), awk는 \0문자열에 최대 하나만 있을 수 있다고 간주하기 때문입니다 . 따라서 각 왼쪽 정규식 tr[i](여기서는 문자열) 이 배열 tr[i+1]의 다음 오른쪽 정규식 으로 대체됩니다 tr.

이 접근 방식을 사용하면 각 쌍에 대해 여러 개의 gsub()를 작성하지 않아도 됩니다.

답변4

모든 Unix 시스템의 모든 쉘에서 awk를 사용하십시오.

$ cat tst.awk
match($0,/START.*END/) {
    tgt = substr($0,RSTART+5,RLENGTH-8)
    sub(/_this_/,"",tgt)
    sub(/modi/,"MODI",tgt)
    gsub(/a/,"4",tgt)
    gsub(/s/,"5",tgt)
    $0 = substr($0,1,RSTART+4) tgt substr($0,RSTART+RLENGTH-3)
}
{ print }

$ awk -f tst.awk file
aomodi3hriq32| ¶³r 0q93aoiSTARTi5_to_be_MODIfied_ENDaqsdofuha23uru| ²23i ii3uhfia
oawpo3<9"§ A hSTART4l5o_need5_MODIfic4tion_ENDqa 032/a237(°1Q"§ >A_this_
START ch4ngeme ENDnot_this_modias

관련 정보