mrb*_*ash 7 bash shell-script text-processing
我有许多文件,每个文件的名称中都包含特定模式,例如ABC1234001
携带有关某些数据组(多列表)的信息。我也有一个info.tsv
这样的表:
group1 ABC1234001 ABC1234010
group2 ABC1234011 ABC1234018
group3 ABC1234019 ABC1234028
... ... ...
Run Code Online (Sandbox Code Playgroud)
它包含了:
所以我需要做的是将每个组的文件合并到一个文件中 - 就像
cat ABC123401{1..8}* >> group2.tsv
Run Code Online (Sandbox Code Playgroud)
以 group2 为例 - 在读取此info.tsv
文件时。在此给定示例中,所有文件 ( ABC1234011.tsv
, ABC1234012.tsv
, ABC1234013.tsv
, ABC1234014.tsv
, ABC1234015.tsv
, ABC1234016.tsv
, ABC1234017.tsv
, ABC1234018.tsv
) 都连接成一个group2.tsv
文件
我要做的事情如下:
group1 ABC1234001 ABC1234010
group2 ABC1234011 ABC1234018
group3 ABC1234019 ABC1234028
... ... ...
Run Code Online (Sandbox Code Playgroud)
但我不太确定如何迭代地更改这种方法的变量。也许使用awk
更有用,但我不知道。该脚本应该生成一堆名为group1.tsv
,的文件group2.tsv
,其中包含表中从“第一个文件”到“最后一个文件”的相应文件的内容。请帮我编写脚本来执行此操作。
下面的脚本假设您可能想要连接的所有文件都与模式匹配*.tsv
。如果您知道它们全部匹配ABC*.tsv
,那么您可能希望在脚本开头使用该模式来代替*.tsv
.
*.tsv
该脚本还假设进入特定组的所有文件名称都生成为扩展至的列表的连续子列表。
#!/bin/sh
set -- *.tsv
while read -r group first last; do
collect=false
for name do
if ! "$collect"; then
[ "$name" = "$first.tsv" ] || continue
collect=true
fi
if "$collect"; then
cat -- "$name"
[ "$name" = "$last.tsv" ] && break
fi
done >"$group.tsv"
done <info.tsv
Run Code Online (Sandbox Code Playgroud)
该脚本将位置参数列表设置为匹配的名称列表*.tsv
。然后,它将每行的三个字段读取info.tsv
到变量group
、first
和中last
。
对于以这种方式读取的每一行info.tsv
,将扫描位置参数列表以查找与组中第一个名称匹配的名称。一旦找到这个名字,我们就设置一个标志 ,collect
它告诉脚本的逻辑从列表中的当前位置开始从位置参数列表中指定的文件收集数据。一旦我们遇到与组的姓氏相对应的名称,这就结束。
请注意,此处true
和false
用作命令而不是简单的字符串。存储在变量中的值$collect
正在执行,if ! "$collect"
这意味着脚本将运行两个 shell 内置命令之一true
或false
. shell 不像其他语言(例如 Python)那样有任何特殊的 true 或 false 关键字。
测试:
$ ls
script
Run Code Online (Sandbox Code Playgroud)
$ touch ABC{1234001..1234030}.tsv
$ for name in ABC*.tsv; do printf 'Name: %s\n' "$name" >"$name"; done
$ cat ABC1234015.tsv
Name: ABC1234015.tsv
Run Code Online (Sandbox Code Playgroud)
$ cat >info.tsv <<END_DATA
group1 ABC1234001 ABC1234010
group2 ABC1234025 ABC1234030
END_DATA
Run Code Online (Sandbox Code Playgroud)
$ ./script
$ cat group1.tsv
Name: ABC1234001.tsv
Name: ABC1234002.tsv
Name: ABC1234003.tsv
Name: ABC1234004.tsv
Name: ABC1234005.tsv
Name: ABC1234006.tsv
Name: ABC1234007.tsv
Name: ABC1234008.tsv
Name: ABC1234009.tsv
Name: ABC1234010.tsv
$ cat group2.tsv
Name: ABC1234025.tsv
Name: ABC1234026.tsv
Name: ABC1234027.tsv
Name: ABC1234028.tsv
Name: ABC1234029.tsv
Name: ABC1234030.tsv
Run Code Online (Sandbox Code Playgroud)
正如对此答案的评论中提到的,我开发此脚本供我个人使用的方式是让脚本看起来像这样:
#!/bin/sh
while read -r group first last; do
collect=false
for name do
filename=$( basename "$name" )
if ! "$collect"; then
[ "$filename" = "$first.tsv" ] || continue
collect=true
fi
if "$collect"; then
cat -- "$name"
[ "$filename" = "$last.tsv" ] && break
fi
done >"$group.tsv"
done
Run Code Online (Sandbox Code Playgroud)
set
请注意顶部命令的删除(这将被命令行参数替换),以及重定向的删除info.tsv
(这将被命令行上的重定向替换)。我还引入了一个filename
变量,它将保存命令行上给出的路径名的文件名部分。
然后我会像这样运行脚本:
$ ./script ABC*.tsv <info.tsv
Run Code Online (Sandbox Code Playgroud)
我用这个实现的是一个脚本,它不知道输入组列表的存储位置或其名称,并且不关心文件的名称ABC
(只要它们具有.tsv
文件名后缀)或它们的存储位置。