我有一个这样的列表:
$$<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)
输入行号大于1print /\$\$</ ? "\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
。如果没有此更新,原始记录将逐字打印,换行符等等。