我有4466.tsv文件,其结构如下:文件_结构
我想比较4466个文件,看看有多少ID(第一列(匹配。我只发现bash命令有两个文件;comm";。你能告诉我怎么做吗?
谢谢
我把你的问题读成:
在所有TSV文件中,在每个文件中都可以找到哪些列ID?
如果这是真的,我们需要所有文件中所有列ID集的交集。我们可以使用join命令来获得任意两个文件的交集,也可以使用交集的代数性质来有效地连接所有文件。
考虑这三个文件的ID的交集:
file1.tsv file2.tsv file3.tsv
--------- --------- ---------
ID ID ID
1 1 2
2 3 3
3
"3〃;是三者之间共享的唯一ID。我们一次只能将两个文件连接在一起,所以我们需要一些方法来有效地获得join (join file1.tsv file2.tsv) file3.tsv
。幸运的是,交集是幂等和关联,所以我们可以在所有文件的循环中迭代应用join,如下所示:
# "Prime" the common file
cp file1.tsv common.tsv
for TSV in file*.tsv; do
join "$TSV" common.tsv > myTmp
mv myTmp common.tsv
echo "After joining $TSV, common IDs are:"
cat common.tsv
done
当我运行它时,它会打印以下内容:
After joining file1.tsv, common IDs are:
ID
1
2
3
After joining file2.tsv, common IDs are:
ID
1
3
After joining file3.tsv, common IDs are:
ID
3
- 第一次迭代将file1与自身连接起来(因为我们用file1启动了common(;这就是我们交集为幂等的地方
- 第二次迭代加入file2;2〃
- 第三次迭代加入file3,将ID缩减为"0";3〃
从技术上讲,join考虑字符串";ID";成为要评估的事情之一。。。它不知道标题行是什么,也不知道ID是什么…它只知道在一些字段中查找公共值。在那个例子中,我们没有指定字段,所以它默认为第一个字段,并且它总是找到";ID";它总是发现";3〃;。
对于您的文件,我们需要告诉加入:
- 在制表符上分隔,带有-t<TAB-CHAR>
- 只输出联接字段(默认情况下,它是第一个字段(,-o为0
以下是我的完整实现:
#!/bin/sh
TAB="$(printf 't')"
# myJoin joins tsvX with the previously-joined common on
# the first field of both files; saving the the first field
# of the joined output back into common
myJoin() {
tsvX="$1"
join -t "$TAB" -o 0 common.tsv "$tsvX" > myTmp.tsv
mv myTmp.tsv common.tsv
}
# "Prime" common
cp input1.tsv common.tsv
for TSV in input*.tsv; do
myJoin "$TSV"
done
echo "The common IDs are:"
tail -n -1 common.tsv
要了解"$(printf 't')"
的原因,请查看以下POSIX合规性:
- https://www.shellcheck.net/wiki/SC3003
- https://unix.stackexchange.com/a/468048/366399
*.tsv
文件的中至少出现一次,则可以在纯Bash中使用关联数组并计算它们的"集交集"来实现这一点(例如(。
#!/bin/bash
# removes all IDs from array $1 that do not occur in array $2.
intersect_ids() {
local -n acc="$1"
local -rn operand="$2"
local id
for id in "${!acc[@]}"; do
((operand["$id"])) || unset "acc['${id}']"
done
}
# prints IDs that occur in all files called *.tsv in directory $1.
get_ids_intersection() (
shopt -s nullglob
local -ar files=("${1}/"*.tsv)
local -Ai common_ids next_ids
local file id _
if ((${#files[@]})); then
while read -r id _; do ((++common_ids["$id"])); done < "${files[0]}"
for file in "${files[@]:1}"; do
while read -r id _; do ((++next_ids["$id"])); done < "$file"
intersect_ids common_ids next_ids
next_ids=()
done
fi
for id in "${!common_ids[@]}"; do printf '%sn' "$id"; done
)
get_ids_intersection /directory/where/tsv/files/are