这个问题来自 如何删除多行文本文件中大括号之间的所有文本?(一样,但是没有嵌套的要求)。
例子:
This is {
{the multiline
text} file }
that wants
{ to {be
changed}
} anyway.
应该变成:
This is
that wants
anyway.
是否可以使用某种单行 bash 命令(awk、sed、perl、grep、cut、tr...等)来完成此操作?
答案1
$ sed ':again;$!N;$!b again; :b; s/{[^{}]*}//g; t b' file3
This is
that wants
anyway.
解释:
:again;$!N;$!b again
这将读取整个文件。
:again
是一个标签。N
读入下一行,并$!N
在尚未读到最后一行的情况下读入下一行。$!b again
分支回到again
标签,条件是这不是最后一行。:b
这定义了一个标签
b
。s/{[^{}]*}//g
只要文本不包含内部大括号,这就会删除大括号中的文本。
t b
如果上述替换命令导致更改,请跳回 label
b
。这样,重复替换命令,直到删除所有大括号组。
答案2
Perl 方法:
$ perl -F"" -a00ne 'for (@F){$i++ if /{/; $i||print; $i-- if /}/}' file
This is
that wants
anyway
解释
-a
-F
:打开数组中给出的文件分隔符的自动分割@F
。-F""
:将输入字段分隔符设置为空,这将导致每个元素成为@F
输入字符之一。-00
:打开“段落模式”,其中“行”定义为两个连续的换行符。这意味着在这种情况下整个文件将被视为一行。如果您的文件可以有多个段落并且括号可以跨越多个段落,请改用-0777
。-ne
:读取输入文件并将给出的脚本应用-e
到每一行。
脚本本身实际上非常简单。每次看到 a 时,计数器就会加一{
,并且每次看到 都会减一}
。这意味着当计数器为 0 时,我们不在括号内,应该打印:
for (@F){}
:对@F
行中的每个元素、每个字符执行此操作。$i++ if /{/;
:$i
如果该字符是一个则加一{
$i||print;
:打印除非$i
已设置(0 视为未设置)。$i-- if /}/
:$i
如果该字符是一个则减一}