还有一个类似的问题:sed search a range and print first set,但对于我的一生,我不知道如何适应我的情况。
如果我定义的配置文件大致如下:
define global {
alias=main
icon_size=20
object_id=0
map_image=net_map_only.jpg
grid_show=0
}
define line {
x=6c4e2f%+10,5dd361%+10
y=6c4e2f%+10,5dd361%+10
z=90
object_id=2d6bec
view_type=line
line_type=11
line_width=1
line_color=#000000
}
#define line {
#x=266424%+10,604bc6%+10
#y=266424%+10,604bc6%+10
#z=90
#object_id=5b6082
#view_type=line
#line_type=11
#line_width=1
#line_color=#000000
#}
define host {
host_name=INTERNET
x=519
y=307
z=98
object_id=6c4e2f
iconset=std_medium
icon_size=20
label_show=1
label_background=#ffffff
label_border=transparent
label_maxlen=14
}
define host {
host_name=INTERNET
x=519
y=307
z=98
object_id=6c4e2f
iconset=std_medium
icon_size=20
label_show=1
label_background=#ffffff
label_border=transparent
label_maxlen=14
}
如上所示,有多个主机条目,还有许多其他条目(定义行,定义全局等)。条目的定义是从正则表达式"^define host"
开始到下一个"}"
。
出于调试目的,我只想选择性地注释或删除第一个主机条目。我将遍历彼此的主机条目并检查错误,因此我想从命令行进行操作。
我的最初想法是:
sed -i '/^define host/,/}/s/^/#/' config.txt
可完美注释所有主机配置行。
但是,我似乎无法获得它,如何适应它,使其仅对第一个匹配的配置条目执行此操作。可能吗
答案 0 :(得分:3)
IIUC:
sed '1,/\}/{s/^/#/}' config.txt
示例输出:
#define host {
#host_name=INTERNET
#x=519
#y=307
#z=98
#object_id=6c4e2f
#iconset=std_medium
#icon_size=20
#label_show=1
#label_background=#ffffff
#label_border=transparent
#label_maxlen=14
#}
define host {
host_name=INTERNET
x=519
y=307
z=98
object_id=6c4e2f
iconset=std_medium
icon_size=20
label_show=1
label_background=#ffffff
label_border=transparent
label_maxlen=14
}
define host {
host_name=INTERNET
x=519
y=307
z=98
object_id=6c4e2f
iconset=std_medium
icon_size=20
label_show=1
label_background=#ffffff
label_border=transparent
label_maxlen=14
}
OP修改了示例输入,并指出define host {
没有
到文件顶部。在这种情况下,以下命令应该起作用:
sed '/^define host/{:a /^$/{:b ;n;bb}; s/^/#/;;n;ba}' config.txt
基于示例输入的示例输出提供了OP:
define global {
alias=main
icon_size=20
object_id=0
map_image=net_map_only.jpg
grid_show=0
}
define line {
x=6c4e2f%+10,5dd361%+10
y=6c4e2f%+10,5dd361%+10
z=90
object_id=2d6bec
view_type=line
line_type=11
line_width=1
line_color=#000000
}
#define line {
#x=266424%+10,604bc6%+10
#y=266424%+10,604bc6%+10
#z=90
#object_id=5b6082
#view_type=line
#line_type=11
#line_width=1
#line_color=#000000
#}
#define host {
#host_name=INTERNET
#x=519
#y=307
#z=98
#object_id=6c4e2f
#iconset=std_medium
#icon_size=20
#label_show=1
#label_background=#ffffff
#label_border=transparent
#label_maxlen=14
#}
define host {
host_name=INTERNET
x=519
y=307
z=98
object_id=6c4e2f
iconset=std_medium
icon_size=20
label_show=1
label_background=#ffffff
label_border=transparent
label_maxlen=14
}
答案 1 :(得分:2)
这可能对您有用(GNU sed):
sed '/^define host/{x;s/^/x/;x};//,/^}/{x;/^x\{1\}$/{x;d};x}' file
在范围的开始处在保留空间中增加一个计数器,并根据范围删除范围,例如这将从行define host
到行}
的行中的行范围中的第一个出现。
此解决方案可以扩展为删除一个范围范围或一组范围:
sed '/^define host/{x;s/^/x/;x};//,/^}/{x;/^x\{2,3\}$/{x;d};x}' file
这将删除上述范围的第二和第三次出现。
sed '/^define host/{x;s/^/x/;x};//,/^}/{x;/^\(x\{2\}\)*$/{x;d};x}' file
这甚至会删除上述范围内的东西。
答案 2 :(得分:2)
ed
非常适合就地编写文件的脚本,而无需像sed
和awk
那样一次处理一行:
ed -s foo.cnf <<EOF
/^define host/;/^}$/ s/^/#/
w
EOF
或在交互式Shell会话中
$ ed -s foo.cnf
/^define host/;/^}$/ s/^/#/
wq
将注释第一块中的每一行,并以define host
开始,最后以}
结尾。请注意,它与您尝试的sed非常相似,但是地址范围仅适用于第一个匹配的块,而不是像sed一样适用于所有匹配的块。
答案 3 :(得分:1)
sed
在单行操作中很有用。尽管您想要的东西可以用sed完成,但这会非常痛苦。
注意,很好地了解sed可能非常有用,本质上它与awk具有相似的脚本语言。但是awk更接近当前的高级语言,并且使用它更容易创建复杂的过滤器。
如果我很了解您的问题,则只需要在所有行之前放置一个“ #
”,直到第一个单行“ }
”为止。 “ define host {
”部分仅是冗余信息(尽管您可以根据需要将其添加到脚本中)。可以使用以下awk脚本轻松完成此操作:
awk '
BEGIN {
PREF="#";
};
{
print PREF $0;
};
/^\}$/ {
PREF="";
};
'
遵循awk的逻辑的最佳做法:
awk
检查所有命令块中的正则表达式。如果匹配,则执行该块。检查执行按照在awk脚本中声明的顺序进行。BEGIN
总是在读取第一行之前被调用,END
在最后一行之后被调用,如果您在块之前不使用正则表达式,则所有行都将被调用。如果需要,您甚至可以在awk中开发复杂的软件,但这不是awk的目的。