带有 ID 数组的单个 zcat 多个提取
Single zcat multiple extracts with IDs arrays
我有很多 GB+ 大小的 gz 档案,由于磁盘 space 的原因我无法解压。每个档案都有一个特定的标识号(例如test365.gz)和这样的结构:
1 1 2 1
########## Name: ZINC000077407198
@<TRIPOS>MOLECULE
ZINC000077407198 none
@<TRIPOS>ATOM
1 C1 5.7064 -2.3998 -12.0246 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000099999999
@<TRIPOS>MOLECULE
ZINC000099999999 none
@<TRIPOS>ATOM
1 C1 -2.0084 -5.2055 -12.9609 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000077402345
@<TRIPOS>MOLECULE
ZINC000077402345 none
@<TRIPOS>ATOM
1 C1 6.5657 -1.5531 -15.3414 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000077407198
@<TRIPOS>MOLECULE
ZINC000077407198 none
@<TRIPOS>ATOM
1 C1 3.6696 -1.8305 -14.6766 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000012345678
@<TRIPOS>MOLECULE
ZINC000012345678 none
@<TRIPOS>ATOM
1 C1 4.5368 -0.8182 -17.4314 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000077407100
@<TRIPOS>MOLECULE
ZINC000077407100 none
@<TRIPOS>ATOM
1 C1 1.4756 -2.2562 -14.0852 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000077407198
@<TRIPOS>MOLECULE
ZINC000077407198 none
@<TRIPOS>ATOM
1 C1 6.1712 -0.8991 -16.4096 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000077407198
@<TRIPOS>MOLECULE
ZINC000077407198 none
@<TRIPOS>ATOM
###### 定义块之间的行数是可变的。
我有 ZINC 实体 + 目标存档的标识符列表:
test365/ ZINC000077407198
test227/ ZINC000009100000
test365/ ZINC000077407100
...
目前我是:
zcat test365.gz | sed -n '/########## Name: ZINC000077407100/,/########## Name:/p' > ZINC000077407100.out
我得到:
########## Name: ZINC000077407100
@<TRIPOS>MOLECULE
ZINC000077407100 none
@<TRIPOS>ATOM
1 C1 1.4756 -2.2562 -14.0852 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000077407198
效果很好。如果 ZINC000077407100 有 N 个块,我会在 zcat 上提取 N 个块,并且不介意以 #####.
开头的行
问题是我需要为 N 个标识符读取存档 N 次/ZINC_NUMBER 我想要有关的信息。而且我要提取成千上万的东西需要很多时间。
所以我想找到一种方法来传递一个数组或标识符列表/ZINC_NUMBER,以根据数组/列表中的标识符将 zcat 读数输出到几个不同的文件。
换句话说,我想使用 zcat 进行单次读取并提取一组标识符的数据,而不仅仅是一个。
感谢您的帮助!
似乎每个以 ##########
开头的条目总是 6 行。在那种情况下,使用 grep -A7
而不是 sed -n /##.../,/##.../p
会更容易和更有效。我想您只打印了后续的 header,因为这样更容易(至少在使用 sed
时)。因此,我在这个答案中排除了后续的 header(grep -A6
而不是 grep -A7
)。
grep
可以给出要搜索的模式列表。这是通过 -f
选项完成的。模式列表可以从您的文件中生成。首先按存档名称分组(例如 test365
),然后打印该存档的所有模式。这里我们使用 awk
来做到这一点。空字节分隔每个存档的模式部分。
为了防止误报(并可能加快搜索速度),我们只搜索完整的行而不是子字符串。为了加快速度,我们设置 LC_ALL=C
。您可能还会发现 zgrep
比 zcat | grep
.
快
以下脚本最多解压每个档案一次。
awk -v prefix='########## Name: ' '
{a[]=a[] "\n" prefix }
END {for (k in a) print k a[k] "[=10=]"}
' /path/to/your/list.txt |
while IFS=$'\n' read -r -d '' archive patterns; do
LC_ALL=C zgrep -A6 -Fxf <(printf %s "$patterns") "${archive/\//.gz}"
# TODO do something with the output for this archive
done
在上面的脚本中,我将 test365/
从您的列表自动转换为 test365.gz
。我不知道你的目录结构。如果您需要不同的东西,请修改 zgrep
的最后一个参数。 $archive
遍历您的 (grouped) 列表的第一列(也就是说,每个档案仅列出一次)。
从您的示例代码来看,您似乎想要为每个模式生成一个单独的文件。为此,将上面的循环 body 替换为
zgrep ... > /tmp/zincfound
while IFS= read -r pattern; do
grep -A6 -Fx "$pattern" /tmp/zincfound > "${pattern##* }.out"
done <<< "$patterns"
rm /tmp/zincfound
每个 OP 的要求是处理大量数据(数百万行,数 GB 的数据,以及需要检索大约 100 个项目的数据)。技术上可以使用现代 bash,但这不太可能表现良好。一个更好的脚本引擎会在这里做得更好。
此处提供了可能的 bash/awk 解决方案。它将扫描每个引用的文件一次,并一次性提取所有选定的标签。请注意 'tags' 列表将被扫描多次,但暗示它的大小是合理的
#! /bin/bash -uex
TAGS=data.txt
file_list=$(awk '{ print }' < $TAGS | sort -u)
for f in $file_list ;
do
gz_name=${f%/}.gz
zcat $gz_name | awk -v F=$f '
# Remember tags to retrieve
!DATA && == F { tags[] = 1 }
# OUT set to current output file, empty if item not selected
DATA && == "##########" && == "Name:" {
OUT = tags[] ? ".out" : "" ;
}
OUT { print >OUT }
' $TAGS DATA=1 -
done
不用说,可以用 Python、Perl、Javascript 或您喜欢的文本处理工具编写上述 5 行 awk 作业。使用示例数据集进行测试。
我有很多 GB+ 大小的 gz 档案,由于磁盘 space 的原因我无法解压。每个档案都有一个特定的标识号(例如test365.gz)和这样的结构:
1 1 2 1
########## Name: ZINC000077407198
@<TRIPOS>MOLECULE
ZINC000077407198 none
@<TRIPOS>ATOM
1 C1 5.7064 -2.3998 -12.0246 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000099999999
@<TRIPOS>MOLECULE
ZINC000099999999 none
@<TRIPOS>ATOM
1 C1 -2.0084 -5.2055 -12.9609 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000077402345
@<TRIPOS>MOLECULE
ZINC000077402345 none
@<TRIPOS>ATOM
1 C1 6.5657 -1.5531 -15.3414 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000077407198
@<TRIPOS>MOLECULE
ZINC000077407198 none
@<TRIPOS>ATOM
1 C1 3.6696 -1.8305 -14.6766 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000012345678
@<TRIPOS>MOLECULE
ZINC000012345678 none
@<TRIPOS>ATOM
1 C1 4.5368 -0.8182 -17.4314 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000077407100
@<TRIPOS>MOLECULE
ZINC000077407100 none
@<TRIPOS>ATOM
1 C1 1.4756 -2.2562 -14.0852 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000077407198
@<TRIPOS>MOLECULE
ZINC000077407198 none
@<TRIPOS>ATOM
1 C1 6.1712 -0.8991 -16.4096 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000077407198
@<TRIPOS>MOLECULE
ZINC000077407198 none
@<TRIPOS>ATOM
###### 定义块之间的行数是可变的。
我有 ZINC 实体 + 目标存档的标识符列表:
test365/ ZINC000077407198
test227/ ZINC000009100000
test365/ ZINC000077407100
...
目前我是:
zcat test365.gz | sed -n '/########## Name: ZINC000077407100/,/########## Name:/p' > ZINC000077407100.out
我得到:
########## Name: ZINC000077407100
@<TRIPOS>MOLECULE
ZINC000077407100 none
@<TRIPOS>ATOM
1 C1 1.4756 -2.2562 -14.0852 C.3 1 LIG1 -0.1500
@<TRIPOS>BOND
1 1 2 1
########## Name: ZINC000077407198
效果很好。如果 ZINC000077407100 有 N 个块,我会在 zcat 上提取 N 个块,并且不介意以 #####.
开头的行问题是我需要为 N 个标识符读取存档 N 次/ZINC_NUMBER 我想要有关的信息。而且我要提取成千上万的东西需要很多时间。
所以我想找到一种方法来传递一个数组或标识符列表/ZINC_NUMBER,以根据数组/列表中的标识符将 zcat 读数输出到几个不同的文件。
换句话说,我想使用 zcat 进行单次读取并提取一组标识符的数据,而不仅仅是一个。
感谢您的帮助!
似乎每个以 ##########
开头的条目总是 6 行。在那种情况下,使用 grep -A7
而不是 sed -n /##.../,/##.../p
会更容易和更有效。我想您只打印了后续的 header,因为这样更容易(至少在使用 sed
时)。因此,我在这个答案中排除了后续的 header(grep -A6
而不是 grep -A7
)。
grep
可以给出要搜索的模式列表。这是通过 -f
选项完成的。模式列表可以从您的文件中生成。首先按存档名称分组(例如 test365
),然后打印该存档的所有模式。这里我们使用 awk
来做到这一点。空字节分隔每个存档的模式部分。
为了防止误报(并可能加快搜索速度),我们只搜索完整的行而不是子字符串。为了加快速度,我们设置 LC_ALL=C
。您可能还会发现 zgrep
比 zcat | grep
.
以下脚本最多解压每个档案一次。
awk -v prefix='########## Name: ' '
{a[]=a[] "\n" prefix }
END {for (k in a) print k a[k] "[=10=]"}
' /path/to/your/list.txt |
while IFS=$'\n' read -r -d '' archive patterns; do
LC_ALL=C zgrep -A6 -Fxf <(printf %s "$patterns") "${archive/\//.gz}"
# TODO do something with the output for this archive
done
在上面的脚本中,我将 test365/
从您的列表自动转换为 test365.gz
。我不知道你的目录结构。如果您需要不同的东西,请修改 zgrep
的最后一个参数。 $archive
遍历您的 (grouped) 列表的第一列(也就是说,每个档案仅列出一次)。
从您的示例代码来看,您似乎想要为每个模式生成一个单独的文件。为此,将上面的循环 body 替换为
zgrep ... > /tmp/zincfound
while IFS= read -r pattern; do
grep -A6 -Fx "$pattern" /tmp/zincfound > "${pattern##* }.out"
done <<< "$patterns"
rm /tmp/zincfound
每个 OP 的要求是处理大量数据(数百万行,数 GB 的数据,以及需要检索大约 100 个项目的数据)。技术上可以使用现代 bash,但这不太可能表现良好。一个更好的脚本引擎会在这里做得更好。
此处提供了可能的 bash/awk 解决方案。它将扫描每个引用的文件一次,并一次性提取所有选定的标签。请注意 'tags' 列表将被扫描多次,但暗示它的大小是合理的
#! /bin/bash -uex
TAGS=data.txt
file_list=$(awk '{ print }' < $TAGS | sort -u)
for f in $file_list ;
do
gz_name=${f%/}.gz
zcat $gz_name | awk -v F=$f '
# Remember tags to retrieve
!DATA && == F { tags[] = 1 }
# OUT set to current output file, empty if item not selected
DATA && == "##########" && == "Name:" {
OUT = tags[] ? ".out" : "" ;
}
OUT { print >OUT }
' $TAGS DATA=1 -
done
不用说,可以用 Python、Perl、Javascript 或您喜欢的文本处理工具编写上述 5 行 awk 作业。使用示例数据集进行测试。