我有一个文件:
"p1"data
"p2"data
"p3"data
"p1"data
"p2"data
"p3"data
"p2"data
"p3"data
"p1"data
我想将所有行与“p1”连接起来,直到遇到下一个“p1”。
"p1"data"p2"data"p3"data
"p1"data"p2"data"p3"data"p2"data"p3"data
"p1"data
引号是字面引号。可能有 1 到 10 组“p2”和“p3” 输入文件有数千行。输出文件应有大约 600 行。
我尝试使用sed -e'/^"p1/N;s/\n//'
,但我需要多次运行它,它最终将“p1”与另一个“p1”连接起来
任何援助将不胜感激。仅供参考,该文件来自 XML 文件。我知道有 xml 工具,但我想使用sed
.
答案1
基于这,使用 GNU (假设环境中sed
没有):$POSIXLY_CORRECT
sed ':a;N;/\n"p1"/!s/\n//;ta;P;D' file
在标准sed
语法中,在分支标签之后甚至不能有任何注释,并且N
在最后一行上运行会丢弃模式空间,因此它必须是:
sed -e :a -e '$!N;/\n"p1"/!s/\n//;ta' -e 'P;D' file
附评论:
sed '
# Label to jump to:
:a
# Append next line to pattern space unless we are
# on the last line:
$!N
# If the newline is NOT followed by "p1", append
# the line by replacing the newline with a space:
/\n"p1"/!s/\n//
# If we changed something, jump to label:
ta
# Print part until newline
P
# Delete part until newline
D' file
答案2
sed 并不是一个好的选择。在每个 Unix 机器上的任何 shell 中使用任何 awk:
$ awk -v ORS= 'NR>1 && /^"p1"/{print RS} {print} END{print RS}' file
"p1"data"p2"data"p3"data
"p1"data"p2"data"p3"data"p2"data"p3"data
"p1"data
答案3
我会使用 perl,而不是 sed - 因为 perl 对多行字符串的支持使得这很容易:
$ perl -0777 -n -e 's/\n//g; s/"p1"/\n$&/g; s/^\n//; print "$_\n"' input.txt
"p1"data"p2"data"p3"data
"p1"data"p2"data"p3"data"p2"data"p3"data
"p1"data
- -0777 告诉perl立即将整个文件读入内存(即“将其“吞入”变量中
$_
) - -n 使 perl 运行类似于
sed -n
(即读取输入而不自动打印任何内容)
该脚本首先删除所有换行符,然后在每个 之前添加一个新行"p1"
,然后删除可能添加到字符串开头的换行符(假设第一行以 开头"p1"
)。
最后,修改后的输入以尾随换行符打印(以便输出对于文本文件有效 - unix 文本文件必须以换行符结尾。许多工具可以很好地处理不以换行符结尾的几乎文本文件换行符,但 a) 有些没有 - 他们无法处理最后一个“行”,因为根据 POSIX,它不以换行符结尾从技术上讲,这不是一条“线”, b) 虽然灵活地接受输入内容很好,但生成正确的输出就更好了,c) 不打印最后的换行符看起来很难看,并导致下一个 shell 提示符与输出出现在同一行,并且d) 类似地,当cat
-ing 多个文件或将文本附加到文件时,它也会导致问题。看在文件末尾添加新行有什么意义?)
或者:
$ perl -0777 -n -e 's/\n//g; s/(.)("p1")/$1\n$2/g; print "$_\n"' input.txt
"p1"data"p2"data"p3"data
"p1"data"p2"data"p3"data"p2"data"p3"data
"p1"data
与第一个版本一样,这会删除所有换行符,但随后它会在"p1"
另一个字符 ( .
) 之后的每个实例之前添加一个换行符 - 即不是第一行。然后它打印修改后的输入并带有尾随换行符。
还有另一种变体:
$ perl -0777 -p -e 's/\n//g; s/(.)("p1")/$1\n$2/g; s/$/\n/' input.txt
"p1"data"p2"data"p3"data
"p1"data"p2"data"p3"data"p2"data"p3"data
"p1"data
这使用 perl 的-p
选项而不是-n
. -p
使 perl 像 sed 一样运行(即读取输入并在任何修改后自动打印它)。否则,它与上面的第二个版本非常相似,但用于s/$/\n/
在自动打印之前在输入末尾添加换行符。
答案4
使用ed
编辑器:
v/^"p1"/ -,. j
这会将不以子字符串开头的每一行"p1"
与上一行连接起来。
这假设第一行以"p1"
子字符串开头。如果不能保证确实如此,请避免v
在第一行运行命令:
2,$ v/^"p1"/ -,. j
对问题中给出的数据进行测试:
$ printf '%s\n' 'v/^"p1"/ -,. j' ,p Q | ed -s file
"p1"data"p2"data"p3"data
"p1"data"p2"data"p3"data"p2"data"p3"data
"p1"data