use*_*746 15 sed awk text-processing
我有一个分布在多个文件中的源代码。
abcdef
,我需要用pqrstuvxyz
.Abcdef
(Sentence Case) 然后它需要替换为Pqrstuvxyz
.AbCdEf
(Toggle case) 然后它需要替换为PqRsTuVxYz
.简而言之,我需要匹配源模式的大小写并应用适当的目标模式。
我如何使用sed
或任何其他工具实现这一目标?
Sté*_*las 10
便携式解决方案使用sed
:
sed '
:1
/[aA][bB][cC][dD][eE][fF]/!b
s//\
&\
pqrstu\
PQRSTU\
/;:2
s/\n[[:lower:]]\(.*\n\)\(.\)\(.*\n\).\(.*\n\)/\2\
\1\3\4/;s/\n[^[:lower:]]\(.*\n\).\(.*\n\)\(.\)\(.*\n\)/\3\
\1\2\4/;t2
s/\n.*\n//;b1'
Run Code Online (Sandbox Code Playgroud)
使用 GNU sed 会容易一些:
search=abcdef replace=pqrstuvwx
sed -r ":1;/$search/I!b;s//\n&&&\n$replace\n/;:2
s/\n[[:lower:]](.*\n)(.)(.*\n)/\l\2\n\1\3/
s/\n[^[:lower:]](.*\n)(.)(.*\n)/\u\2\n\1\3/;t2
s/\n.*\n(.*)\n/\1/g;b1"
Run Code Online (Sandbox Code Playgroud)
通过使用&&&
上面,我们重用字符串替换的其余部分的情况下的模式,因此ABcdef
将改为PQrstuVWx
和AbCdEf
到PqRsTuVwX
。将其更改为&
仅影响前 6 个字符的大小写。
(请注意,如果替换可能会被替换(例如,如果替换foo
forfoo
或bcd
for abcd
),它可能无法执行您想要的操作,或者可能会陷入无限循环
便携式解决方案使用awk
:
awk -v find=abcdef -v rep=pqrstu '{
lwr=tolower($0)
offset=index(lwr, tolower(find))
if( offset > 0 ) {
printf "%s", substr($0, 0, offset)
len=length(find)
for( i=0; i<len; i++ ) {
out=substr(rep, i+1, 1)
if( substr($0, offset+i, 1) == substr(lwr, offset+i, 1) )
printf "%s", tolower(out)
else
printf "%s", toupper(out)
}
printf "%s\n", substr($0, offset+len)
}
}'
Run Code Online (Sandbox Code Playgroud)
示例输入:
awk -v find=abcdef -v rep=pqrstu '{
lwr=tolower($0)
offset=index(lwr, tolower(find))
if( offset > 0 ) {
printf "%s", substr($0, 0, offset)
len=length(find)
for( i=0; i<len; i++ ) {
out=substr(rep, i+1, 1)
if( substr($0, offset+i, 1) == substr(lwr, offset+i, 1) )
printf "%s", tolower(out)
else
printf "%s", toupper(out)
}
printf "%s\n", substr($0, offset+len)
}
}'
Run Code Online (Sandbox Code Playgroud)
示例输出:
other abcdef other
other Abcdef other
other AbCdEf other
Run Code Online (Sandbox Code Playgroud)
正如评论中指出的那样,上面的内容只会替换find
每一行中的第一个实例。替换所有实例:
awk -v find=abcdef -v rep=pqrstu '{
input=$0
lwr=tolower(input)
offset=index(lwr, tolower(find))
if( offset > 0 ) {
while( offset > 0 ) {
printf "%s", substr(input, 0, offset)
len=length(find)
for( i=0; i<len; i++ ) {
out=substr(rep, i+1, 1)
if( substr(input, offset+i, 1) == substr(lwr, offset+i, 1) )
printf "%s", tolower(out)
else
printf "%s", toupper(out)
}
input=substr(input, offset+len)
lwr=substr(lwr, offset+len)
offset=index(lwr, tolower(find))
}
print input
}
}'
Run Code Online (Sandbox Code Playgroud)
示例输入:
other abcdef other ABCdef other
other Abcdef other abcDEF
other AbCdEf other aBCdEf other
Run Code Online (Sandbox Code Playgroud)
示例输出:
other pqrstu other PQRstu other
other Pqrstu other pqrSTU
other PqRsTu other pQRsTu other
Run Code Online (Sandbox Code Playgroud)
你可以使用perl
. 直接来自常见问题解答 - 引用自perldoc perlfaq6
:
如何在 LHS 上不区分大小写替换,同时在 RHS 上保留大小写?
这是 Larry Rosler 的一个可爱的 Perlish 解决方案。它利用 ASCII 字符串上的按位异或的属性。
$_= "this is a TEsT case";
$old = 'test';
$new = 'success';
s{(\Q$old\E)}
{ uc $new | (uc $1 ^ $1) .
(uc(substr $1, -1) ^ substr $1, -1) x
(length($new) - length $1)
}egi;
print;
Run Code Online (Sandbox Code Playgroud)
这里它是一个子程序,仿照上述:
sub preserve_case($$) {
my ($old, $new) = @_;
my $mask = uc $old ^ $old;
uc $new | $mask .
substr($mask, -1) x (length($new) - length($old))
}
$string = "this is a TEsT case";
$string =~ s/(test)/preserve_case($1, "success")/egi;
print "$string\n";
Run Code Online (Sandbox Code Playgroud)
这打印:
this is a SUcCESS case
Run Code Online (Sandbox Code Playgroud)
作为替代方案,如果替换词比原始词长,则要保留替换词的大小写,您可以使用 Jeff Pinyan 编写的以下代码:
sub preserve_case {
my ($from, $to) = @_;
my ($lf, $lt) = map length, @_;
if ($lt < $lf) { $from = substr $from, 0, $lt }
else { $from .= substr $to, $lf }
return uc $to | ($from ^ uc $from);
}
Run Code Online (Sandbox Code Playgroud)
这会将句子更改为“这是一个成功案例”。
只是为了表明 C 程序员可以用任何编程语言编写 C,如果您更喜欢更像 C 的解决方案,以下脚本使替换具有与原始相同的大小写,一个字母一个字母。(它的运行速度也恰好比 Perlish 解决方案的运行速度慢 240%。)如果替换的字符数比被替换的字符串多,则替换的其余部分使用最后一个字符的大小写。
# Original by Nathan Torkington, massaged by Jeffrey Friedl
#
sub preserve_case($$)
{
my ($old, $new) = @_;
my ($state) = 0; # 0 = no change; 1 = lc; 2 = uc
my ($i, $oldlen, $newlen, $c) = (0, length($old), length($new));
my ($len) = $oldlen < $newlen ? $oldlen : $newlen;
for ($i = 0; $i < $len; $i++) {
if ($c = substr($old, $i, 1), $c =~ /[\W\d_]/) {
$state = 0;
} elsif (lc $c eq $c) {
substr($new, $i, 1) = lc(substr($new, $i, 1));
$state = 1;
} else {
substr($new, $i, 1) = uc(substr($new, $i, 1));
$state = 2;
}
}
# finish up with any remaining new (for when new is longer than old)
if ($newlen > $oldlen) {
if ($state == 1) {
substr($new, $oldlen) = lc(substr($new, $oldlen));
} elsif ($state == 2) {
substr($new, $oldlen) = uc(substr($new, $oldlen));
}
}
return $new;
}
Run Code Online (Sandbox Code Playgroud)
如果您将替换修剪为pqrstu
,请尝试以下操作:
输入:
abcdef
Abcdef
AbCdEf
ABcDeF
Run Code Online (Sandbox Code Playgroud)
输出:
$ perl -lpe 's/$_/$_^lc($_)^"pqrstu"/ei' file
pqrstu
Pqrstu
PqRsTu
PQrStU
Run Code Online (Sandbox Code Playgroud)
如果你想用 替换prstuvxyz
,可能是这样的:
$ perl -lne '@c=unpack("(A4)*",$_);
$_ =~ s/$_/$_^lc($_)^"pqrstu"/ei;
$c[0] =~ s/$c[0]/$c[0]^lc($c[0])^"vxyz"/ei;
print $_,$c[0]' file
pqrstuvxyz
PqrstuVxyz
PqRsTuVxYz
PQrStUVXyZ
Run Code Online (Sandbox Code Playgroud)
我找不到任何映射规则ABcDeF
-> PQrStUvxyz
。