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
之间的子字符串(并且仅是子字符串)应该像我使用 一样进行修改。START
END
sed '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
withFS="START|END"
失败,因为OFS
不能在不同位置设置多个值。
我尝试使用sed
嵌套命令替换和不同的分隔符 ( ) 但失败了,并且还担心之前/之后~
可能有字符会扰乱命令(例如 a )。我们的想法是仅选择“内部”子字符串并执行操作,然后将其用作替换的一部分:START
END
/
sed "s/^\(.*\)START.*END\(.*\)$/\1$(sed 's~^.*START~~
s~END.*~~
s~_this_~~
s~modi~MODI~
y~as~45~' infile)\2/" infile
我不熟悉例如perl
....但无论如何。
有没有办法使一组sed
- 操作仅应用于行的 REGEX 匹配子字符串?
答案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
-CSD
将输入从 UTF-8 解码并将输出编码为 UTF-8- 我们可以使用and来代替填充三个变量
$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=....
;右手映射也不应该有一些特殊字符,例如&
Ì back-references\1
等;然而,当您手动编写映射时,您可以转义任何特殊字符,以避免它们被 gsub() 专门解释。
我使用 CR\r
来分隔映射,正如您提到的,这是输入文件中唯一不存在的东西,除此之外,\0
它不能在 split() 和 awk 中的其他函数(或者也可能在其他编程语言中)中使用,因为 awk 会只考虑\0
字符串中最多可以存在一个。因此,每个左侧正则表达式tr[i]
(此处为字符串)都将替换为数组tr[i+1]
中的下一个右侧正则表达式tr
。
使用这种方法可以让您不必为每一对编写多个 gsub() 。
答案4
在每个 Unix 机器上的任何 shell 中使用任何 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