如何使用 sed/perl/awk 查找和替换模式字符串?
How to find and replace a pattern string using sed/perl/awk?
我有一个文件foo.properties,其内容类似于
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.03,delta:1.0,gamma:.5
在我的脚本中,我需要替换 ph
的任何值(bash 脚本不知道当前值)并将其更改为 0.5。所以文件应该看起来像
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5
我知道如果通过使用知道当前值就可以轻松完成
sed "s/\,ph\:0.03\,/\,ph\:0.5\,/" foo.properties
但就我而言,我必须实际阅读 allNames
的内容并搜索值,然后在 for 循环中替换。其余的都已处理,但我无法为此弄清楚 sed
/perl
命令。
我尝试使用 sed "s/\,ph\:.*\,/\,ph\:0.5\,/" foo.properties
和一些变体,但没有用。
使用您显示的示例,请尝试以下 awk
代码。
awk -v new_val="0.5" '
match([=10=],/,ph:[0-9]+(\.[0-9]+)?/){
val=substr([=10=],RSTART+1,RLENGTH-1)
sub(/:.*/,":",val)
print substr([=10=],1,RSTART) val new_val substr([=10=],RSTART+RLENGTH)
next
}
1
' Input_file
详细说明: 创建 awk
的名为 new_val
的变量,其中包含需要的新值在awk
的主程序中使用awk
的match
函数在每一行匹配,ph:[0-9]+(\.[0-9]+)?
正则表达式,如果找到正则表达式的匹配则将匹配的值存储到变量 val
。然后在此处用 :
替换 val 变量中从 :
到值结尾的所有内容。然后打印值作为 OP 的预先要求(匹配正则表达式值之前的值与 val(在正则表达式中编辑的匹配值)与新值和行的其余部分),使用 next 将避免进一步并通过提及 1
打印其余行其中没有匹配的值。
第二种解决方案:使用awk
.
的sub
函数
awk -v newVal="0.5" '/^allNames=/{sub(/,ph:[^,]*/,",ph:"newVal)} 1' Input_file
给你
#!/usr/bin/perl
use strict;
use warnings;
print "\nPerl Starting ... \n\n";
while (my $recordLine =<DATA>)
{
chomp($recordLine);
if (index($recordLine, "ph:") != -1)
{
$recordLine =~ s/ph:.*?,/ph:0.5,/g;
print "recordLine: $recordLine ...\n";
}
}
print "\nPerl End ... \n\n";
__DATA__
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.03,delta:1.0,gamma:.5
输出:
Perl Starting ...
recordLine: allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5 ...
Perl End ...
行中的任何位置是否有小数位,或没有值。
sed -E 's/(^|[^-_[:alnum:]])ph:[0-9]*(.[0-9]+)?/ph:0.5/g'
或者可能:
sed -E 's/(^|[=,[:space:]])ph:[0-9]+(.[0-9]+)?/ph:0.5/g'
顶部使用“不是其他命名字符”来描述紧接在名称之前的字符,底部使用定界符(您可以向其中任何一个添加更多字符)。目的是避免与 other_ph
或 autograph
.
冲突
请您尝试一个 perl
解决方案:
perl -pe '
s/(?<=\bph:)[\d.]+(?=,|$)/0.5/;
' foo.properties
-pe
选项使perl
逐行读取输入,执行
操作,然后像 sed
那样打印它。
- 正则表达式
(?<=\bph:)
是一个零长度的 lookbehind,它匹配
字符串 ph:
前面有单词边界。
- 正则表达式
[\d.]+
将匹配十进制数。
- 正则表达式
(?=,|$)
是一个零长度先行匹配
逗号或字符串结尾。
- 由于lookbehind和lookahead的长度为零,所以它们不是
由
s/../../
运算符替换。
[编辑]
正如 Dave Cross 评论的那样,只要输入文件格式正确,就不需要先行 (?=,|$)
。
更简单的 sed 解决方案:
sed -E 's/([=,]ph:)[0-9.]+/.5/g' file
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5
这里我们匹配 ([=,]ph:)
(即 ,
或 =
后跟 ph:
)并在第 1 组中捕获。这后面应该跟 1+ of [0-9.]
字符来匹配任何数字。作为替代,我们将 </code> 放回 <code>0.5
在每个 Unix 机器上的任何 shell 中使用任何 sed(发布的其他使用 sed -E
的 sed 解决方案需要 GNU 或 BSD seds):
a) 如果 ph:
永远不是 allNames
列表中的第一个标签(如您的示例输入所示):
$ sed 's/\(,ph:\)[^,]*/.5/' foo.properties
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5
b) 或者如果可以先:
$ sed 's/\([,=]ph:\)[^,]*/.5/' foo.properties
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5
我有一个文件foo.properties,其内容类似于
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.03,delta:1.0,gamma:.5
在我的脚本中,我需要替换 ph
的任何值(bash 脚本不知道当前值)并将其更改为 0.5。所以文件应该看起来像
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5
我知道如果通过使用知道当前值就可以轻松完成
sed "s/\,ph\:0.03\,/\,ph\:0.5\,/" foo.properties
但就我而言,我必须实际阅读 allNames
的内容并搜索值,然后在 for 循环中替换。其余的都已处理,但我无法为此弄清楚 sed
/perl
命令。
我尝试使用 sed "s/\,ph\:.*\,/\,ph\:0.5\,/" foo.properties
和一些变体,但没有用。
使用您显示的示例,请尝试以下 awk
代码。
awk -v new_val="0.5" '
match([=10=],/,ph:[0-9]+(\.[0-9]+)?/){
val=substr([=10=],RSTART+1,RLENGTH-1)
sub(/:.*/,":",val)
print substr([=10=],1,RSTART) val new_val substr([=10=],RSTART+RLENGTH)
next
}
1
' Input_file
详细说明: 创建 awk
的名为 new_val
的变量,其中包含需要的新值在awk
的主程序中使用awk
的match
函数在每一行匹配,ph:[0-9]+(\.[0-9]+)?
正则表达式,如果找到正则表达式的匹配则将匹配的值存储到变量 val
。然后在此处用 :
替换 val 变量中从 :
到值结尾的所有内容。然后打印值作为 OP 的预先要求(匹配正则表达式值之前的值与 val(在正则表达式中编辑的匹配值)与新值和行的其余部分),使用 next 将避免进一步并通过提及 1
打印其余行其中没有匹配的值。
第二种解决方案:使用awk
.
sub
函数
awk -v newVal="0.5" '/^allNames=/{sub(/,ph:[^,]*/,",ph:"newVal)} 1' Input_file
给你
#!/usr/bin/perl
use strict;
use warnings;
print "\nPerl Starting ... \n\n";
while (my $recordLine =<DATA>)
{
chomp($recordLine);
if (index($recordLine, "ph:") != -1)
{
$recordLine =~ s/ph:.*?,/ph:0.5,/g;
print "recordLine: $recordLine ...\n";
}
}
print "\nPerl End ... \n\n";
__DATA__
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.03,delta:1.0,gamma:.5
输出:
Perl Starting ...
recordLine: allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5 ...
Perl End ...
行中的任何位置是否有小数位,或没有值。
sed -E 's/(^|[^-_[:alnum:]])ph:[0-9]*(.[0-9]+)?/ph:0.5/g'
或者可能:
sed -E 's/(^|[=,[:space:]])ph:[0-9]+(.[0-9]+)?/ph:0.5/g'
顶部使用“不是其他命名字符”来描述紧接在名称之前的字符,底部使用定界符(您可以向其中任何一个添加更多字符)。目的是避免与 other_ph
或 autograph
.
请您尝试一个 perl
解决方案:
perl -pe '
s/(?<=\bph:)[\d.]+(?=,|$)/0.5/;
' foo.properties
-pe
选项使perl
逐行读取输入,执行 操作,然后像sed
那样打印它。- 正则表达式
(?<=\bph:)
是一个零长度的 lookbehind,它匹配 字符串ph:
前面有单词边界。 - 正则表达式
[\d.]+
将匹配十进制数。 - 正则表达式
(?=,|$)
是一个零长度先行匹配 逗号或字符串结尾。 - 由于lookbehind和lookahead的长度为零,所以它们不是
由
s/../../
运算符替换。
[编辑]
正如 Dave Cross 评论的那样,只要输入文件格式正确,就不需要先行 (?=,|$)
。
更简单的 sed 解决方案:
sed -E 's/([=,]ph:)[0-9.]+/.5/g' file
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5
这里我们匹配 ([=,]ph:)
(即 ,
或 =
后跟 ph:
)并在第 1 组中捕获。这后面应该跟 1+ of [0-9.]
字符来匹配任何数字。作为替代,我们将 </code> 放回 <code>0.5
在每个 Unix 机器上的任何 shell 中使用任何 sed(发布的其他使用 sed -E
的 sed 解决方案需要 GNU 或 BSD seds):
a) 如果 ph:
永远不是 allNames
列表中的第一个标签(如您的示例输入所示):
$ sed 's/\(,ph:\)[^,]*/.5/' foo.properties
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5
b) 或者如果可以先:
$ sed 's/\([,=]ph:\)[^,]*/.5/' foo.properties
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5