连接由模式分隔的连续行组的技术

连接由模式分隔的连续行组的技术

我有一个这样的列表:

$$<002L_tbfl
Putative transcription factor 001R;
GO:0006355
GO:0046782
GO:0006351
IPR007031
$$<002L_FRG3G
Uncharacterized protein 002L;
GO:0033644
GO:0016021
IPR004251

我希望每个都$$<开始一个新行,并将以下条目放在同一行上(制表符分隔),直到$$<再次出现。像这样:

$$<002L_tbfl    Putative transcription factor 001R; GO:0006355  GO:0046782  GO:0006351  IPR007031
$$<002L_FRG3G   Uncharacterized protein 002L;   GO:0033644  GO:0016021  IPR004251

到目前为止我的方法是:

tr '\n' '\t'   < stage1 > stage2
sed 's/$$</\n/g' stage2 > stage3

问题是,上面的方法在小文件上完全可以正常工作,但在我的 4 GB 文件上它似乎可以工作,然后在很短的时间内返回一个空白文件,没有错误或消息。
我也试过了tr '$$<' '\n',还是不行;它会产生一个奇怪的文件。

答案1

以下是如何做到这一点sed

sed -n '/$$</! H; /$$</{x; s/\n/\t/gp}; ${x; s/\n/\t/gp}' stage1 > stage3

分成几部分:

  • sed -n意味着不打印默认输出(即经过处理的输入);仅当有p命令时才打印。
  • /$$</! H意味着当你看到一条线时不是包含$$<,将其附加到“H旧空间”(即暂存区域)。这!反转了正常逻辑,意思是“对不满足此条件的行执行此操作”。如果您需要忽略$$<发生在行中间的情况,请更改此设置(并且下一个命令)以使用/^$$</. (如果您需要以$$<不同的方式在行中间进行处理 - 例如,在其之前插入换行符 - 编辑您的问题以说明这一点。)

    如果在保留空间中已有内容(在保留空间中)时向保留空间追加一行,则sed在它们之间插入换行符,这样将在保留空间中构建如下所示的文本:

    $$<002L_tbflnewline推定转录因子 001R;newline去:0006355…

    保留空间,如“模式空间”(普通的工作行缓冲区) 一般来说末尾不会有显式换行符(它是隐式的)。当然,可以在空格中显式插入换行符。

  • /$$</{…}表示在包含 的行上执行大括号内的命令$$<

    • x意味着 eX更改保留空间和模式空间的内容。
    • s/\n/\t/gp意思是——嗯,这很明显,不是吗? — 这意味着用制表符替换换行符(在模式空间中) G全球范围内和p打印结果。

    当此命令读取输入的第一行(其中包含$$<)时,会x将该行 ( $$<002L_tbfl) 从模式空间移动到保留空间,并将保留空间的先前内容移动到模式空间。但是,由于保留空间的初始内容什么都没有,这意味着该命令没有任何作用s。随后,当您看到时$$<(例如,在第 7 行),它将带有嵌入换行符的文本(如上所示)带入模式空间,并(如上所述)用制表符替换所有换行符并打印结果。

  • ${…}表示当到达输入末尾时执行大括号内的命令。这些命令与我们看到$$<, 时执行的命令相同,用于从保留空间中清除最后一行(即最后一行)。

警告:不保证这适用于 POSIX sed。我已经在 GNU 上测试过它sed

答案2

$ cat ip.txt 
$$<002L_tbfl
Putative transcription factor 001R;
GO:0006355
GO:0046782
GO:0006351
IPR007031
$$<002L_FRG3G
Uncharacterized protein 002L;
GO:0033644
GO:0016021
IPR004251

$ perl -ne 'chomp if !eof; if($. > 1){print /\$\$</ ? "\n" : "\t"} print' ip.txt 
$$<002L_tbfl    Putative transcription factor 001R; GO:0006355  GO:0046782  GO:0006351  IPR007031
$$<002L_FRG3G   Uncharacterized protein 002L;   GO:0033644  GO:0016021  IPR004251
  • chomp if !eof从除文件最后一行之外的所有输入行中删除换行符
  • if($. > 1)输入行号大于1
  • print /\$\$</ ? "\n" : "\t"如果行匹配则添加换行符$$<,否则添加制表符
  • print打印输入行

答案3

大概存在 32 位限制,因此只能进行流处理。您可以使用awk,如

awk 'NR==1 {printf "%s",$0; next;} $1~/^\$\$</ {printf "\n%s",$0; next;} {printf "\t%s",$0;}' < file

这将连续打印所有输入行,不带换行符,除了以$$<初始换行符开头的行(第一行之后)。

也许您想要最后一个换行符,这需要 和 END 节。请参阅man awk这些变化。

答案4

可与 Ubuntu 12 上的 Mawk 和 Gawk 3.x 配合使用,支持RS正则表达式。

$ awk 'BEGIN { RS="\\$\\$<"; FS="\n"; OFS="\t" } NF && $1="$$<"$1' data

输出:

$$<002L_tbfl    Putative transcription factor 001R; GO:0006355  GO:0046782  GO:0006351  IPR007031   
$$<002L_FRG3G   Uncharacterized protein 002L;   GO:0033644  GO:0016021  IPR004251

我们简单地使用$$<作为记录分隔符和换行符作为字段分隔符。

这意味着:

  • 由于输入以记录分隔符开头,因此我们得到一个空记录。我们通过使用NF以下条件来消除这个问题:字段数量必须非零。
  • $$<从输入中删除。我们把它放回$1.

要打印中间带有制表符的字段,我们将制表符设置为输出字段分隔符 ( OFS)。 { print }是模式的默认操作,因此我们忽略它。

我们修改的事实$1还会产生副作用,$0即通过使用 联接所有字段来更新记录变量OFS。如果没有此更新,原始记录将逐字打印,换行符等等。

相关内容