我坚持下去。因此,我的代码中有一个while读取循环,它花了很长时间,我想在许多处理器中运行它。但是,我想分割输入文件并运行14个循环(因为我有14个线程),每个分割的文件并行一个。问题是我不知道如何告诉while循环获取和使用哪个文件。
例如,在常规的while读取循环中,我将编写代码:
while read line
do
<some code>
done < input file or variable...
但是在这种情况下,我想将上面的输入文件分成14个文件,并并行运行14个while循环,每个分割的文件一个。 我试过了:
split -n 14 input_file
find . -name "xa*" | \
parallel -j 14 | \
while read line
do
<lot of stuff>
done
也尝试过
split -n 14 input_file
function loop {
while read line
do
<lot of stuff>
done
}
export -f loop
parallel -j 14 ::: loop
但是我都不知道哪个文件是循环的输入,因此并行将理解“将每个xa *文件并并行放置到各个循环中”
输入文件的示例(字符串列表)
AEYS01000010.10484.12283
CVJT01000011.50.2173
KF625180.1.1799
KT949922.1.1791
LOBZ01000025.54942.57580
编辑
这是代码。 输出是一个表格(741100行),其中包含有关已经进行的DNA序列比对的一些统计信息。 循环使用一个带有DNA序列伪点的input_file(无折线,从500到〜45000行,从800Kb变化),逐行读取它,并查找数据库中这些伪点的每个对应的完整分类法(〜45000行) 。然后,它会做一些和/除。输出是.tsv,看起来像这样(序列“ KF625180.1.1799”的示例):
Rate of taxonomies for this sequence in %: KF625180.1.1799 D_6__Bacillus_atrophaeus
Taxonomy %aligned number_ocurrences_in_the_alignment num_ocurrences_in_databank %alingment/databank
D_6__Bacillus_atrophaeus 50% 1 20 5%
D_6__Bacillus_amyloliquefaciens 50% 1 154 0.649351%
$ head input file
AEYS01000010.10484.12283
CVJT01000011.50.217
KF625180.1.1799
KT949922.1.1791
LOBZ01000025.54942.57580
循环中还使用了两个其他文件。它们不是循环输入。 1)名为alnout_file的文件,该文件仅用于查找给定序列对数据库造成的命中(或比对)次数。它也是以前在此循环之外制作的。它的行数可以从单行到数千行不等。此处仅第1列和第2列很重要。 Column1是序列的名称,而col2是它在databnk中匹配的所有序列的名称。看起来像这样:
$ head alnout_file
KF625180.1.1799 KF625180.1.1799 100.0 431 0 0 1 431 1 431 -1 0
KF625180.1.1799 KP143082.1.1457 99.3 431 1 2 1 431 1 429 -1 0
KP143082.1.1457 KF625180.1.1799 99.3 431 1 2 1 429 1 431 -1 0
2)一个数据库.tsv文件,其中包含对应于DNA序列的〜45000个分类法。每个分类法都在一行中:
$ head taxonomy.file.tsv
KP143082.1.1457 D_0__Bacteria;D_1__Firmicutes;D_2__Bacilli;D_3__Bacillales;D_4__Bacillaceae;D_5__Bacillus;D_6__Bacillus_amyloliquefaciens
KF625180.1.1799 D_0__Bacteria;D_1__Firmicutes;D_2__Bacilli;D_3__Bacillales;D_4__Bacillaceae;D_5__Bacillus;D_6__Bacillus_atrophaeus
因此,给定序列KF625180.1.1799。我之前将其与包含约45000个其他DNA序列的数据库进行比对,并获得了输出,其中包含与之匹配的序列的所有附件。循环的作用是找到所有这些序列的分类法,并计算出我之前提到的“统计量”。对我拥有的所有DNA序列配件都进行编码。
TAXONOMY=path/taxonomy.file.tsv
while read line
do
#find hits
hits=$(grep $line alnout_file | cut -f 2)
completename=$(grep $line $TAXONOMY | sed 's/D_0.*D_4/D_4/g')
printf "\nRate of taxonomies for this sequence in %%:\t$completename\n"
printf "Taxonomy\t%aligned\tnumber_ocurrences_in_the_alignment\tnum_ocurrences_in_databank\t%alingment/databank\n"
#find hits and calculate the frequence (%) of the taxonomy in the alignment output
# ex.: Bacillus_subtilis 33
freqHits=$(grep "${hits[@]}" $TAXONOMY | \
cut -f 2 | \
awk '{a[$0]++} END {for (i in a) {print i, "\t", a[i]/NR*100, "\t", a[i]}}' | \
sed -e 's/D_0.*D_5/D_5/g' -e 's#\s\t\s#\t#g' | \
sort -k2 -hr)
# print frequence of each taxonomy in the databank
freqBank=$(while read line; do grep -c "$line" $TAXONOMY; done < <(echo "$freqHits" | cut -f 1))
#print cols with taxonomy and calculations
paste <(printf %s "$freqHits") <(printf %s "$freqBank") | awk '{print $1,"\t",$2"%","\t",$3,"\t",$4,"\t",$3/$4*100"%"}'
done < input_file
这需要大量的时间和解析,因此在一个处理器中运行大约12h即可完成所有45000个DNA序列登录。我想将input_file分割并在我拥有的所有处理器(14)中进行处理,因为这会花费时间。 谢谢大家对我这么耐心=)
答案 0 :(得分:1)
作为替代方案,我进行了一次快速测试。
#! /bin/env bash
mkfifo PIPELINE # create a single queue
cat "$1" > PIPELINE & # supply it with records
{ declare -i cnt=0 max=14
while (( ++cnt <= max )) # spawn loop creates worker jobs
do printf -v fn "%02d" $cnt
while read -r line # each work loop reads common stdin...
do echo "$fn:[$line]"
sleep 1
done >$fn.log 2>&1 & # these run in background in parallel
done # this one exits
} < PIPELINE # *all* read from the same queue
wait
cat [0-9][0-9].log
不需要split
,但是需要mkfifo
。
很显然,请在内部循环中更改代码。
答案 1 :(得分:1)
这回答了您的要求,即如何并行处理从运行split
获得的14个文件。但是,我不认为这是做您想做的事情的最佳方法-但是我们将需要您的一些答案。
因此,让我们制作一个百万行的文件并将其分为14个部分:
seq 1000000 > 1M
split -n 14 1M part-
这给了我14个名为part-aa
至part-an
的文件。现在您的问题是如何并行处理这14个部分-(首先阅读最后一行):
#!/bin/bash
# This function will be called for each of the 14 files
DoOne(){
# Pick up parameters
job=$1
file=$2
# Count lines in specified file
lines=$(wc -l < "$file")
echo "Job No: $job, file: $file, lines: $lines"
}
# Make the function above known to processes spawned by GNU Parallel
export -f DoOne
# Run 14 parallel instances of "DoOne" passing job number and filename to each
parallel -k -j 14 DoOne {#} {} ::: part-??
示例输出
Job No: 1, file: part-aa, lines: 83861
Job No: 2, file: part-ab, lines: 72600
Job No: 3, file: part-ac, lines: 70295
Job No: 4, file: part-ad, lines: 70295
Job No: 5, file: part-ae, lines: 70294
Job No: 6, file: part-af, lines: 70295
Job No: 7, file: part-ag, lines: 70295
Job No: 8, file: part-ah, lines: 70294
Job No: 9, file: part-ai, lines: 70295
Job No: 10, file: part-aj, lines: 70295
Job No: 11, file: part-ak, lines: 70295
Job No: 12, file: part-al, lines: 70294
Job No: 13, file: part-am, lines: 70295
Job No: 14, file: part-an, lines: 70297
您通常会忽略 GNU Parallel 的-k
参数-我只是添加了它,因此输出是按顺序排列的。
答案 2 :(得分:1)
您正在寻找--pipe
。在这种情况下,您甚至可以使用经过优化的--pipepart
(版本> 20160621):
export TAXONOMY=path/taxonomy.file.tsv
doit() {
while read line
do
#find hits
hits=$(grep $line alnout_file | cut -f 2)
completename=$(grep $line $TAXONOMY | sed 's/D_0.*D_4/D_4/g')
printf "\nRate of taxonomies for this sequence in %%:\t$completename\n"
printf "Taxonomy\t%aligned\tnumber_ocurrences_in_the_alignment\tnum_ocurrences_in_databank\t%alingment/databank\n"
#find hits and calculate the frequence (%) of the taxonomy in the alignment output
# ex.: Bacillus_subtilis 33
freqHits=$(grep "${hits[@]}" $TAXONOMY | \
cut -f 2 | \
awk '{a[$0]++} END {for (i in a) {print i, "\t", a[i]/NR*100, "\t", a[i]}}' | \
sed -e 's/D_0.*D_5/D_5/g' -e 's#\s\t\s#\t#g' | \
sort -k2 -hr)
# print frequence of each taxonomy in the databank
freqBank=$(while read line; do grep -c "$line" $TAXONOMY; done < <(echo "$freqHits" | cut -f 1))
#print cols with taxonomy and calculations
paste <(printf %s "$freqHits") <(printf %s "$freqBank") | awk '{print $1,"\t",$2"%","\t",$3,"\t",$4,"\t",$3/$4*100"%"}'
done
}
export -f doit
parallel -a input_file --pipepart doit
这会将输入文件切成10 * ncpu个块(其中ncpu是CPU线程数),将每个块传递给doit
,并行运行ncpu作业。
那就是说,我认为您的真正问题是产生了太多的程序:如果您用Perl或Python重写doit
,我希望您会看到一个重大的提速。
答案 3 :(得分:1)
我认为在这里使用一堆grep
和awk
命令是错误的方法-使用Perl或awk可能会更好。由于您没有提供任何示例文件,因此我使用以下代码生成了一些文件:
#!/bin/bash
for a in {A..Z} {0..9} ; do
for b in {A..Z} {0..9} ; do
for c in {A..Z} {0..9} ; do
echo "${a}${b}${c}"
done
done
done > a
# Now make file "b" which has the same stuff but shuffled into a different order
gshuf < a > b
请注意,字母中有26个字母,因此,如果将数字0..9添加到字母中,则会得到36个字母数字,如果嵌套3个循环,则会得到36^3
或46,656行,大致与您的文件大小匹配。文件a
现在看起来像这样:
AAA
AAB
AAC
AAD
AAE
AAF
文件b
如下所示:
UKM
L50
AOC
79U
K6S
6PO
12I
XEV
WJN
现在,我想遍历a
,在b
中找到相应的行。首先,我使用您的方法:
time while read thing ; do grep $thing b > /dev/null ; done < a
这需要 9分钟35秒。
如果我现在在第一场比赛中退出grep
,平均而言,我会在中间找到它,这意味着时间将减少一半,因为我以后不会继续不必要地阅读b
找到我想要的。
time while read thing ; do grep -m1 $thing b > /dev/null ; done < a
这可以将时间缩短至 4分30秒。
如果我现在使用awk
将b
的内容读入一个关联数组(也称为哈希),然后读取a
的元素并在b
中找到它们像这样:
time awk 'FNR==NR{a[$1]=$1; next} {print a[$1]}' b a > /dev/null
现在运行时间为0.07秒。希望你对我开车的想法有所了解。我希望Perl可以同时执行此操作,并且还可以在循环中间为数学提供更多的表达功能。
答案 4 :(得分:0)
我希望这个小脚本可以帮助您:
function process {
while read line; do
echo "$line"
done < $1
}
function loop {
file=$1
chunks=$2
dir=`mktemp -d`
cd $dir
split -n l/$chunks $file
for i in *; do
process "$i" &
done
rm -rf $dir
}
loop /tmp/foo 14
它以指定数量的块(无分割行)并行地在指定文件上运行过程循环(使用&
将每个调用置于后台)。希望它能帮助您入门。
答案 5 :(得分:0)
这可以为您完成工作,我对并行并不熟悉,而是使用本机bash生成过程&
:
function loop () {
while IFS= read -r -d $'\n'
do
# YOUR BIG STUFF
done < "${1}"
}
arr_files=(./xa*)
for i in "${arr_files[@]}"
do loop "${i}" &
done
wait