如何使用 paste/join 或 linux 或 perl 以有序方式有效地加入 'n' 个文件?
How to join 'n' number of files in ordered way efficiently using paste/join or linux or perl?
数以千计的文件以 *.tab 结尾。每个文件的第一列是 header。每个文件都有自己的 header(所以它们是不同的)。我不介意从任何文件中得到一个 header。
所有文件的行数都相等,因此有一个顺序。我想要的输出具有相同的顺序。
目录中的示例文件
test_1.tab
test_2.tab
.
.
.
.
test_1990.tab
test_2000.tab
test_1.tab
Pro_01 0 0 0 0 0 1 1 1 0 1 1 0 .....0
Pro_02 0 0 0 0 0 1 1 0 0 0 0 0 .....1
Pro_03 1 1 1 1 1 0 0 1 0 1 1 0 .....1
.
.
.
Pro_200 0 0 0 0 1 1 1 1 1 1 0 .....0
test_2000.tab
Pro_1901 1 1 1 1 0 1 1 0 0 0 0 1 .....0
Pro_1902 1 1 1 0 0 0 1 0 0 0 0 0 .....1
Pro_1903 1 1 0 1 0 1 0 0 0 0 0 1 .....1
.
.
.
Pro_2000 1 0 0 0 0 1 1 1 1 1 0 .....0
期望的输出
Pro_01 0 0 0 0 0 1 1 1 0 1 1 0 0 ..... 1 1 1 1 0 1 1 0 0 0 0 1 0
Pro_02 0 0 0 0 0 1 1 0 0 0 0 0 1 ..... 1 1 1 0 0 0 1 0 0 0 0 0 1
Pro_03 1 1 1 1 1 0 0 1 0 1 1 0 1 ..... 1 1 0 1 0 1 0 0 0 0 0 1 1
.
.
.
Pro_200 0 0 0 0 1 1 1 1 1 1 0 0 ..... 1 0 0 0 0 1 1 1 1 1 0 0
我的代码
for i in *.tab/; do paste allCol.tab <(cut -f 2- "$i") > itermediate.csv; mv intermediate.csv allCol.tab ; done
paste <(cut -f1 test1.tab) allCol.tab > final.tab
rm allCol.tab
大约需要 3 个小时。哪种方法更好?
另外,是否有任何其他命令可以交叉检查此输出文件与所有输入文件?喜欢 diff 还是 wc?
试试这个。
#!/bin/bash
TMP=tmp
mkdir "$TMP"
RESULT=result
#read each file and append the contents of each line in them
#to a new file for each line in the tmp directory
for f in *.tab; do
i=1
while read -r l; do
echo "$l" >> "$TMP"/"$i"
((i++))
done < <(cut -f2- "$f")
done
#integrate each file in tmp dir into a single line of the $RESULT file
exec 1>>$RESULT
for f in "$TMP"/*; do
while read -r l; do
printf '%s\t' "$l"
done < <(cat "$f")
echo
done
rm -r "$TMP"
这个算法可以在多个处理器上拆分,任务完成得更快。
您还可以向其中添加诸如检查 $TMP
是否已成功创建之类的内容。
递归函数是一个很好的工具。作为第一次剪辑——简短但简单:
pasteAll() {
first=; shift
case $# in
0) cut -f 2- "$first" ;;
*) paste <(cut -f 2- "$first") <(pasteAll "$@") ;;
esac
}
set -- *.tab
paste <(cut -f 1 "") <(pasteAll "$@")
检查是否包含所有文件和行——如果每个输入文件包含相同数量的行——就像检查输出文件的行数和最后一行的列数一样简单。
数以千计的文件以 *.tab 结尾。每个文件的第一列是 header。每个文件都有自己的 header(所以它们是不同的)。我不介意从任何文件中得到一个 header。
所有文件的行数都相等,因此有一个顺序。我想要的输出具有相同的顺序。
目录中的示例文件
test_1.tab
test_2.tab
.
.
.
.
test_1990.tab
test_2000.tab
test_1.tab
Pro_01 0 0 0 0 0 1 1 1 0 1 1 0 .....0
Pro_02 0 0 0 0 0 1 1 0 0 0 0 0 .....1
Pro_03 1 1 1 1 1 0 0 1 0 1 1 0 .....1
.
.
.
Pro_200 0 0 0 0 1 1 1 1 1 1 0 .....0
test_2000.tab
Pro_1901 1 1 1 1 0 1 1 0 0 0 0 1 .....0
Pro_1902 1 1 1 0 0 0 1 0 0 0 0 0 .....1
Pro_1903 1 1 0 1 0 1 0 0 0 0 0 1 .....1
.
.
.
Pro_2000 1 0 0 0 0 1 1 1 1 1 0 .....0
期望的输出
Pro_01 0 0 0 0 0 1 1 1 0 1 1 0 0 ..... 1 1 1 1 0 1 1 0 0 0 0 1 0
Pro_02 0 0 0 0 0 1 1 0 0 0 0 0 1 ..... 1 1 1 0 0 0 1 0 0 0 0 0 1
Pro_03 1 1 1 1 1 0 0 1 0 1 1 0 1 ..... 1 1 0 1 0 1 0 0 0 0 0 1 1
.
.
.
Pro_200 0 0 0 0 1 1 1 1 1 1 0 0 ..... 1 0 0 0 0 1 1 1 1 1 0 0
我的代码
for i in *.tab/; do paste allCol.tab <(cut -f 2- "$i") > itermediate.csv; mv intermediate.csv allCol.tab ; done
paste <(cut -f1 test1.tab) allCol.tab > final.tab
rm allCol.tab
大约需要 3 个小时。哪种方法更好? 另外,是否有任何其他命令可以交叉检查此输出文件与所有输入文件?喜欢 diff 还是 wc?
试试这个。
#!/bin/bash
TMP=tmp
mkdir "$TMP"
RESULT=result
#read each file and append the contents of each line in them
#to a new file for each line in the tmp directory
for f in *.tab; do
i=1
while read -r l; do
echo "$l" >> "$TMP"/"$i"
((i++))
done < <(cut -f2- "$f")
done
#integrate each file in tmp dir into a single line of the $RESULT file
exec 1>>$RESULT
for f in "$TMP"/*; do
while read -r l; do
printf '%s\t' "$l"
done < <(cat "$f")
echo
done
rm -r "$TMP"
这个算法可以在多个处理器上拆分,任务完成得更快。
您还可以向其中添加诸如检查 $TMP
是否已成功创建之类的内容。
递归函数是一个很好的工具。作为第一次剪辑——简短但简单:
pasteAll() {
first=; shift
case $# in
0) cut -f 2- "$first" ;;
*) paste <(cut -f 2- "$first") <(pasteAll "$@") ;;
esac
}
set -- *.tab
paste <(cut -f 1 "") <(pasteAll "$@")
检查是否包含所有文件和行——如果每个输入文件包含相同数量的行——就像检查输出文件的行数和最后一行的列数一样简单。