替换行之间的模式
Replace a pattern between lines
我正在尝试替换文件行之间的模式。
具体来说,我想在大型和多个文件中将 ,\n &
替换为 , &\n
。这实际上将符号 & 移动到上一行。使用 CTR+H 这很容易,但我发现使用 sed 很难。
所以,初始文件的格式如下:
A +,
& B -,
& C ),
& D +,
& E (,
& F *,
# & G -,
& H +,
& I (,
& J +,
K ?,
输出形式为:
A +, &
B -, &
C ), &
D +, &
E (, &
F *, &
# & G -,
H +, &
I (, &
J +,
K ?,
根据之前关于 Whosebug 的回答问题,我尝试使用以下命令对其进行转换:
sed ':a;N;$!ba;s/,\n &/&\n /g' file1.txt > file2.txt
sed -i -e '$!N;/&/b1' -e 'P;D' -e:1 -e 's/\n[[:space:]]*/ /' file2.txt
但如果文件中存在符号“#”,它们就会失败。
有什么方法可以更简单的替换匹配的模式,比方说:
sed -i 's/,\n &/, &\n /g' file
提前致谢!
使用sed
$ sed ':a;N;s/\n \+\(&\) \(.*\)/ \n /;ba' input_file
A +, &
B -, &
C ), &
D +, &
E (, &
F *,
# & G -, &
H +, &
I (, &
J +,
如果您使用 GNU sed
并且您的文件不包含 NUL 字符(ASCII 代码 0),您可以使用其 -z
选项将整个文件作为一个字符串处理,并且multi-line 替代命令的模式(m
标志)。 m
标志不是绝对需要的,但它简化了一点(.
和字符 类 不匹配换行符):
$ sed -Ez ':a;s/((\`|\n)[^#]*,)((\n.*#.*)*)(\n[[:blank:]]*)&/ \& /gm;ta' file
A +, &
B -, &
C ), &
D +, &
E (, &
F *, &
# & G -,
H +, &
I (, &
J +,
K ?,
这对应于您的文本说明和您展示的示例所需的输出。但这有点复杂。它不处理以换行符结尾的行,而是处理以换行符(或文件开头)开头并在下一个换行符之前结束的 sub-strings 。让我们将这些命名为“chunks”。
我们搜索形式为 AB*C
的块序列,其中:
A
是一个不包含 #
的块(可能是第一个)。它由 (\<backtick>|\n)[^#]*,
匹配,这意味着 beginning-of-file-or-newline,后跟除换行符和 #
之外的任意数量的字符,后跟一个逗号。
B*
是包含 #
的任意数量(包括 none)块。它由 \n.*#.*
匹配,这意味着换行符,后跟除换行符之外的任意数量的字符,然后是 #
和除换行符之外的任意数量的字符。
C
是一个以换行符开头的块,然后是 spaces 和 &
。它由 \n[[:blank:]]*&
匹配,这意味着换行符,后跟任意数量的空格和 &
.
如果我们找到这样一个 AB*C
序列我们在 A
的末尾添加一个 space 和一个 &
,我们不改变 B*
,然后我们将 C
中的第一个 &
替换为 space。然后我们重复,直到找不到这样的序列。
注意:如果逗号后面可以跟换行符之前的空格,我们必须考虑到它们。如果你想保留它们:
$ sed -Ez ':a;s/((\`|\n)[^#]*,[[:blank:]]*)((\n.*#.*)*)(\n[[:blank:]]*)&/ \& /gm;ta' file
其他:
$ sed -Ez ':a;s/((\`|\n)[^#]*,)[[:blank:]]*((\n.*#.*)*)(\n[[:blank:]]*)&/ \& /gm;ta' file
假设行
# & G -,
是一个注释行,稍后可能会取消注释,处理该行中的 &
也可能有意义。不知道数据的用途,这可能有用也可能没用。
用GNUawk,命令
awk 'BEGIN { RS=",";ORS="" } { printf "%s%s", ORS, gensub(/(\n[ \t#]*)&/, " \&\1 ",1); ORS=RS }' inputfile
会转入
A +,
& B -,
& C ),
& D +,
& E (,
& F *,
# & G -,
& H +,
& I (,
& J +,
K ?,
进入
A +, &
B -, &
C ), &
D +, &
E (, &
F *, &
# G -, &
H +, &
I (, &
J +,
K ?,
此脚本只有在最后一行以换行符终止或者,
.
后有任何其他字符时才能正确运行
解释:
RS=","
将逗号设置为记录分隔符而不是输入的换行符。
ORS=""
将输出记录分隔符设置为第一条记录之前的空字符串。
fprintf "%s%s", ORS, gensub(...)
pre 挂起记录分隔符而不是追加它。
gensub
GNU 特定的替换函数,允许反向引用匹配的组。
/(\n[ \t#]*)&/
搜索模式:括号定义了一个组 (1),它由一个换行符 \n
后跟任何 space 序列、制表符或注释字符 [ \t#]*
.该组后跟一个 &
字符。
" \&\1 "
替换:space 后跟 &
,后跟捕获组 (1) (\1
) 和一个额外的 space 来替换已删除 &
。 (\&
是获取文字 &
字符而不是插入整个匹配项所必需的。)
ORS=RS
将输出记录分隔符设置为第一行后的 ,
。 (事实上,在每个 ros 之后)在第二个和后续记录之前添加一个逗号。这确保了应该是换行符的最后一条记录不会得到尾随 ,
.
GNU Awk 脚本版本低于
如果输入文件的最后一行 not 以换行符终止,则将按预期 only 工作。
它将创建一个带有 ,
的附加行,因为包含换行符的最后一条记录将由输出记录分隔符 ,
.
终止
awk 'BEGIN { RS=ORS="," } { print gensub(/(\n[ \t#]*)&/, " \&\1 ",1) }' inputfile
如果输入文件以换行符结尾,输出将是
...
I (, &
J +,
K ?,
,
在最后一个 ,
之后没有换行符。
使用 sed
sed -En 'H;${g;s/^\n//;s/((\n *#.*)*)\n +&(.*)/ \&\n /gmp}' file
说明
-E
启用扩展正则表达式
-n
阻止 sed 的默认打印
H
追加保留space
${
结束时
g
将保留 space 中的内容覆盖为模式 space
s/^\n//;
从保留中删除前导换行符 space
s/
开始替补
((\n *#.*)*)
捕获组 1,可选择重复匹配换行符和 # 后跟行的其余部分
\n +&(.*)
匹配换行符和 1+ spaces,然后匹配 &
并捕获组 3 中的其余行
/
在此之后替换为
\&\n
包含捕获组和转义 &
的替换模式
/
结束替换
gmp
g局部替换所有出现,m多行,p打印有替换的行
输出
A +, &
B -, &
C ), &
D +, &
E (, &
F *, &
# & G -,
H +, &
I (, &
J +,
K ?,%
看到一个bash demo.
这可能适合您 (GNU sed):
sed -E '/,$/{:a;N;/#[^\n]*$/ba
s/,((\n.*)*)\n(\s*)&/, \&\n /;h;s/(.*)\n.*//p;g;s/.*\n(.*\n)//;D}' file
形成两行 window(但如有必要,也包括注释)。
格式化第一行并打印它(如果找到则带有注释)。
删除除最后两行以外的所有行。
删除左边两行的第一行并重复。
我正在尝试替换文件行之间的模式。
具体来说,我想在大型和多个文件中将 ,\n &
替换为 , &\n
。这实际上将符号 & 移动到上一行。使用 CTR+H 这很容易,但我发现使用 sed 很难。
所以,初始文件的格式如下:
A +,
& B -,
& C ),
& D +,
& E (,
& F *,
# & G -,
& H +,
& I (,
& J +,
K ?,
输出形式为:
A +, &
B -, &
C ), &
D +, &
E (, &
F *, &
# & G -,
H +, &
I (, &
J +,
K ?,
根据之前关于 Whosebug 的回答问题,我尝试使用以下命令对其进行转换:
sed ':a;N;$!ba;s/,\n &/&\n /g' file1.txt > file2.txt
sed -i -e '$!N;/&/b1' -e 'P;D' -e:1 -e 's/\n[[:space:]]*/ /' file2.txt
但如果文件中存在符号“#”,它们就会失败。
有什么方法可以更简单的替换匹配的模式,比方说:
sed -i 's/,\n &/, &\n /g' file
提前致谢!
使用sed
$ sed ':a;N;s/\n \+\(&\) \(.*\)/ \n /;ba' input_file
A +, &
B -, &
C ), &
D +, &
E (, &
F *,
# & G -, &
H +, &
I (, &
J +,
如果您使用 GNU sed
并且您的文件不包含 NUL 字符(ASCII 代码 0),您可以使用其 -z
选项将整个文件作为一个字符串处理,并且multi-line 替代命令的模式(m
标志)。 m
标志不是绝对需要的,但它简化了一点(.
和字符 类 不匹配换行符):
$ sed -Ez ':a;s/((\`|\n)[^#]*,)((\n.*#.*)*)(\n[[:blank:]]*)&/ \& /gm;ta' file
A +, &
B -, &
C ), &
D +, &
E (, &
F *, &
# & G -,
H +, &
I (, &
J +,
K ?,
这对应于您的文本说明和您展示的示例所需的输出。但这有点复杂。它不处理以换行符结尾的行,而是处理以换行符(或文件开头)开头并在下一个换行符之前结束的 sub-strings 。让我们将这些命名为“chunks”。
我们搜索形式为 AB*C
的块序列,其中:
A
是一个不包含#
的块(可能是第一个)。它由(\<backtick>|\n)[^#]*,
匹配,这意味着 beginning-of-file-or-newline,后跟除换行符和#
之外的任意数量的字符,后跟一个逗号。B*
是包含#
的任意数量(包括 none)块。它由\n.*#.*
匹配,这意味着换行符,后跟除换行符之外的任意数量的字符,然后是#
和除换行符之外的任意数量的字符。C
是一个以换行符开头的块,然后是 spaces 和&
。它由\n[[:blank:]]*&
匹配,这意味着换行符,后跟任意数量的空格和&
.
如果我们找到这样一个 AB*C
序列我们在 A
的末尾添加一个 space 和一个 &
,我们不改变 B*
,然后我们将 C
中的第一个 &
替换为 space。然后我们重复,直到找不到这样的序列。
注意:如果逗号后面可以跟换行符之前的空格,我们必须考虑到它们。如果你想保留它们:
$ sed -Ez ':a;s/((\`|\n)[^#]*,[[:blank:]]*)((\n.*#.*)*)(\n[[:blank:]]*)&/ \& /gm;ta' file
其他:
$ sed -Ez ':a;s/((\`|\n)[^#]*,)[[:blank:]]*((\n.*#.*)*)(\n[[:blank:]]*)&/ \& /gm;ta' file
假设行
# & G -,
是一个注释行,稍后可能会取消注释,处理该行中的 &
也可能有意义。不知道数据的用途,这可能有用也可能没用。
用GNUawk,命令
awk 'BEGIN { RS=",";ORS="" } { printf "%s%s", ORS, gensub(/(\n[ \t#]*)&/, " \&\1 ",1); ORS=RS }' inputfile
会转入
A +,
& B -,
& C ),
& D +,
& E (,
& F *,
# & G -,
& H +,
& I (,
& J +,
K ?,
进入
A +, &
B -, &
C ), &
D +, &
E (, &
F *, &
# G -, &
H +, &
I (, &
J +,
K ?,
此脚本只有在最后一行以换行符终止或者,
.
解释:
RS=","
将逗号设置为记录分隔符而不是输入的换行符。ORS=""
将输出记录分隔符设置为第一条记录之前的空字符串。fprintf "%s%s", ORS, gensub(...)
pre 挂起记录分隔符而不是追加它。gensub
GNU 特定的替换函数,允许反向引用匹配的组。/(\n[ \t#]*)&/
搜索模式:括号定义了一个组 (1),它由一个换行符\n
后跟任何 space 序列、制表符或注释字符[ \t#]*
.该组后跟一个&
字符。" \&\1 "
替换:space 后跟&
,后跟捕获组 (1) (\1
) 和一个额外的 space 来替换已删除&
。 (\&
是获取文字&
字符而不是插入整个匹配项所必需的。)ORS=RS
将输出记录分隔符设置为第一行后的,
。 (事实上,在每个 ros 之后)在第二个和后续记录之前添加一个逗号。这确保了应该是换行符的最后一条记录不会得到尾随,
.
GNU Awk 脚本版本低于
如果输入文件的最后一行 not 以换行符终止,则将按预期 only 工作。
它将创建一个带有 ,
的附加行,因为包含换行符的最后一条记录将由输出记录分隔符 ,
.
awk 'BEGIN { RS=ORS="," } { print gensub(/(\n[ \t#]*)&/, " \&\1 ",1) }' inputfile
如果输入文件以换行符结尾,输出将是
...
I (, &
J +,
K ?,
,
在最后一个 ,
之后没有换行符。
使用 sed
sed -En 'H;${g;s/^\n//;s/((\n *#.*)*)\n +&(.*)/ \&\n /gmp}' file
说明
-E
启用扩展正则表达式-n
阻止 sed 的默认打印H
追加保留space${
结束时g
将保留 space 中的内容覆盖为模式 spaces/^\n//;
从保留中删除前导换行符 spaces/
开始替补((\n *#.*)*)
捕获组 1,可选择重复匹配换行符和 # 后跟行的其余部分\n +&(.*)
匹配换行符和 1+ spaces,然后匹配&
并捕获组 3 中的其余行
/
在此之后替换为\&\n
包含捕获组和转义&
的替换模式
/
结束替换gmp
g局部替换所有出现,m多行,p打印有替换的行
输出
A +, &
B -, &
C ), &
D +, &
E (, &
F *, &
# & G -,
H +, &
I (, &
J +,
K ?,%
看到一个bash demo.
这可能适合您 (GNU sed):
sed -E '/,$/{:a;N;/#[^\n]*$/ba
s/,((\n.*)*)\n(\s*)&/, \&\n /;h;s/(.*)\n.*//p;g;s/.*\n(.*\n)//;D}' file
形成两行 window(但如有必要,也包括注释)。
格式化第一行并打印它(如果找到则带有注释)。
删除除最后两行以外的所有行。
删除左边两行的第一行并重复。