如何连接相交的线直到出现相同的模式

如何连接相交的线直到出现相同的模式

我有一个文件:

 "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

相关内容