说明:使用 GNU sed。
现状 - ~/.screenrc,最后几行:
# name synthpop
# screen -t script emacs -nw /home/$USER/bin/synthpop
# name thrashmetal
# screen -t script emacs -nw /home/$USER/bin/thrashmetal
# number 1
# split -v
# focus
# chdir "/home/$USER/thrashmetal/src"
# screen -t scr watch nl asdkjlek.html
# number 2
# focus
# split
# focus
# screen -t output watch thrashmetal asdkjlek.html
# number 3
# focus
# split
# focus
# chdir "/home/$USER/thrashmetal/plan"
# screen -t soll watch less soll
# number 4
# focus
# name darkwave
# screen -t script emacs -nw /home/$USER/bin/darkwave
目的:
# name synthpop
# screen -t script emacs -nw /home/$USER/bin/synthpop
name thrashmetal
screen -t script emacs -nw /home/$USER/bin/thrashmetal
number 1
split -v
focus
chdir "/home/$USER/thrashmetal/src"
screen -t scr watch nl asdkjlek.html
number 2
focus
split
focus
screen -t output watch thrashmetal asdkjlek.html
number 3
focus
split
focus
chdir "/home/$USER/thrashmetal/plan"
screen -t soll watch less soll
number 4
focus
# name darkwave
# screen -t script emacs -nw /home/$USER/bin/darkwave
我当前的尝试几乎可以完成工作,但一旦出现该行就无法停止,这只是一个新行,没有任何注释符号或字母数字字符:
sed 's+^# \(name thrashmetal\)+\1+;:a;s+# ++;ta' .screenrc
解释、意图:
'
一系列要做的事情的开始s
代替+
使用“+”作为分隔符# …
逐行浏览,直到找到以“#”开头的行...\(
启动组以供以后反向参考name thrashmetal
并继续“名称thrashmetal”\)
组结束+\1+
仅用组部分替换该行中的第一个匹配项,不带“#”;
然后,从那时起,做另一件事……:a
做一个名为“a”的循环;s
对于接下来的每一行,您逐行替换......+
设置“+”作为分隔符#
该行中“#”的第一个匹配项,无论该行中其后的内容如何...++
什么都没有,有效地删除它;ta
只要你失败了就去做应该是当你到达段落末尾时,然后是分隔 thrashmetal 块和 darkwave 块的 2 个换行符,然后返回到“a”所在的位置'
一系列要做的事情的结束.screenrc
执行有关我当前目录中的文件“.screenrc”的一系列操作 - 我的主目录
标准输出:
# name synthpop
# screen -t script emacs -nw /home/$USER/bin/synthpop
name thrashmetal
screen -t script emacs -nw /home/$USER/bin/thrashmetal
number 1
split -v
focus
chdir "/home/$USER/thrashmetal/src"
screen -t scr watch nl asdkjlek.html
number 2
focus
split
focus
screen -t output watch thrashmetal asdkjlek.html
number 3
focus
split
focus
chdir "/home/$USER/thrashmetal/plan"
screen -t soll watch less soll
number 4
focus
name darkwave
screen -t script emacs -nw /home/$USER/bin/darkwave
如何解决这个问题?我做错了什么,循环不会在到达 thrashmetal 段落末尾时立即停止,而是简单地跳到下一段(darkwave 段落)并继续?
我猜想,那个循环中的任何东西都一定是错误的……也许是“t”,但“T”或“b”也不会剪掉它……
答案1
sed '/^# name thrashmetal$/,/^$/ s/^# //' file
这将应用替换,#
从范围内的每一行中删除初始和后续的空格字符。
该范围以行匹配开始^# name thrashmetal$
,以第一个后续行匹配^$
(空行)结束。
或者,直接翻译成awk
:
awk '/^# name thrashmetal$/,/^$/ { sub(/^# /, "") }; 1' file
此处,尾随1
是{ print $0 }
or的{ print }
缩写,导致输出当前(可能已修改)行(文本的打印在sed
脚本中是隐式的)。
对于任何对ed
编辑器感兴趣的人,您可以使用以下编辑命令来解决此问题:
/^# name thrashmetal$/;.,/^$/ s/^# //
这会将光标移动到所需段落的第一行,并将替换应用于该行以及后面的所有行,直到到达空行。
我们不能使用
/^# name thrashmetal$/,/^$/ s/^# //
...因为地址是相对于当前行计算的,并且当我们启动编辑器时当前行位于文件的末尾。由于我们位于文件的末尾,因此命令中的范围将无效,因为地址/^$/
将是比起始地址更早的行(相对于当前行,第一个空行位于段落开头之前) 。
您自己的方法已更正(将分隔符从加号更改为默认值并分成多个表达式,这更便携):
sed -e '/^# \(name thrashmetal\)/!b' -e 's//\1/' -e :a -e n -e 's/^# //' -e ta file
我没有进行初始替换,而是让段落初始行的匹配来确定是否跳过编辑脚本的其余部分。b
如果未遇到该行,则跳过该行。
如果找到该行,我们将对其进行修改并创建标签a
。然后,我们打印当前缓冲区并使用下一行替换其内容n
。循环像以前一样继续,#
如果缓冲区的内容发生变化,则删除子字符串并循环回标签。
您最初尝试的主要问题是,
sed 's+^# \(name thrashmetal\)+\1+;:a;s+# ++;ta' .screenrc
……就是那个“a
-loop”是无条件的,因此适用于输入的每一行,无论初始替换是否成功或失败(您显示前两行不受命令影响,但我认为这是一个复制粘贴错误)。循环也不会读取下一行,因此它只会运行一次每行(除非#
在一行上找到多个子字符串)。
我的修复方法是将初始替换分为测试和替换,然后确保“ a
-loop”读取下一行输入,直到替换失败。我也不会替换#
任何一行上的多个或嵌入(内部)字符串,只会替换行开头的第一个字符串。
答案2
这个脚本似乎可以做你想做的事:
H
/^$/ T show
$ T show
d
:show
x
s/\n//
/^# name thrashmetal/ {
s/# //
s/\n# /\n/g
}
我们有效地将事物分割成空行分隔的块。当我们找到以 开头的块时# name thrashmetal
,我们删除#
行开头的所有字符串。
给定您的示例输入,这将产生输出:
$ sed -f filter.sed < example.conf
# name synthpop
# screen -t script emacs -nw /home/$USER/bin/synthpop
name thrashmetal
screen -t script emacs -nw /home/$USER/bin/thrashmetal
number 1
split -v
focus
chdir "/home/$USER/thrashmetal/src"
screen -t scr watch nl asdkjlek.html
number 2
focus
split
focus
screen -t output watch thrashmetal asdkjlek.html
number 3
focus
split
focus
chdir "/home/$USER/thrashmetal/plan"
screen -t soll watch less soll
number 4
focus
# name darkwave
# screen -t script emacs -nw /home/$USER/bin/darkwave
带注释的 sed 脚本:
# Append current line to the hold space
H
# If we find a blank line or reach EOF, print out the
# current chunk
/^$/ T show
$ T show
# Delete the current line (we'll print it later in the
# show routine)
d
:show
# Swap the pattern space and the hold space
x
# Remove the initial "\n" that was added when we appended
# the first line with the H command
s/\n//
# If this is our target chunk, remove the comments
/^# name thrashmetal/ {
s/# //
s/\n# /\n/g
}
答案3
您还可以考虑使用 awk:
$ awk -v RS= -v ORS='\n\n' '/thrashmetal/{sub(/^# /,"");gsub(/\n# /,"\n",$0)}1' .screenrc
# name synthpop
# screen -t script emacs -nw /home/$USER/bin/synthpop
name thrashmetal
screen -t script emacs -nw /home/$USER/bin/thrashmetal
number 1
split -v
focus
chdir "/home/$USER/thrashmetal/src"
screen -t scr watch nl asdkjlek.html
number 2
focus
split
focus
screen -t output watch thrashmetal asdkjlek.html
number 3
focus
split
focus
chdir "/home/$USER/thrashmetal/plan"
screen -t soll watch less soll
number 4
focus
# name darkwave
# screen -t script emacs -nw /home/$USER/bin/darkwave
在这里,通过将 RS(输入记录分隔符)设置为空白,您可以将段落视为单行。
答案4
使用乐(以前称为 Perl_6)
~$ raku -ne 'if / thrashmetal /fff^/ \# \s name / { .subst(/^ "\# " /).put } else { $_.put };' file
或者:
~$ raku -ne 'put (/ thrashmetal /fff^/ \# \s name /) ?? $_.subst(/^ "\# " /) !! $_ ;' file
Raku 是 Perl 家族的一种编程语言。您可以使用 Raku-ne
类似 awk 的非自动打印命令行标志和 if/else 语句来更改所选行。
这里的关键是使用Raku的/…ON…/fffˆ/…OFF…/
“触发器”运算符,当第一个识别域匹配时翻转ON,然后当第二个识别域匹配时翻转OFF。基于fff
,这里我们添加一个尾随插入^
符号,指示 Raku 删除第二个匹配行。
因此,您可以一次匹配所需的注释掉记录/段落,用于thrashmetal
第一个匹配和\# \s name
第二个匹配。使用 取消注释已识别的记录.subst(/^ "\# " /)
。
输入示例:
# name synthpop
# screen -t script emacs -nw /home/$USER/bin/synthpop
# name thrashmetal
# screen -t script emacs -nw /home/$USER/bin/thrashmetal
# number 1
# split -v
# focus
# chdir "/home/$USER/thrashmetal/src"
# screen -t scr watch nl asdkjlek.html
# number 2
# focus
# split
# focus
# screen -t output watch thrashmetal asdkjlek.html
# number 3
# focus
# split
# focus
# chdir "/home/$USER/thrashmetal/plan"
# screen -t soll watch less soll
# number 4
# focus
# name darkwave
# screen -t script emacs -nw /home/$USER/bin/darkwave
示例输出:
# name synthpop
# screen -t script emacs -nw /home/$USER/bin/synthpop
name thrashmetal
screen -t script emacs -nw /home/$USER/bin/thrashmetal
number 1
split -v
focus
chdir "/home/$USER/thrashmetal/src"
screen -t scr watch nl asdkjlek.html
number 2
focus
split
focus
screen -t output watch thrashmetal asdkjlek.html
number 3
focus
split
focus
chdir "/home/$USER/thrashmetal/plan"
screen -t soll watch less soll
number 4
focus
# name darkwave
# screen -t script emacs -nw /home/$USER/bin/darkwave