Bash shell for 循环中的任务

Bash shell task within for loop

我有两个文件:

temp_bandstructure.dat 具有以下格式

# spin    band          kx          ky          kz          E(MF)          E(QP)        Delta E kn  E(MF)5dp
#                        (Cartesian coordinates)             (eV)           (eV)           (eV)     (eV)
     1      22     0.00000     0.00000     0.00000   -3.021665798   -4.022414204   -1.000748406 1   -3.02167
     1      22     0.00850     0.00000     0.00000   -3.026245712   -4.027334803   -1.001089091 2   -3.02625
     1      22     0.01699     0.00000     0.00000   -3.039924052   -4.061680485   -1.021756433 3   -3.03992
9000 more rows

mf_pband.dat 有 46 header 行,后跟以下

  1     0.00000   -55.55593   0.998   0.000 ...20 more columns
9000 more rows

我有一个嵌套的 for 循环,用于比较 mf_pband.dat 中每一行的第 1 列和第 3 列与 temp_bandstructure.dat 中每一行的第 9 列和第 10 列。如果匹配值在 0.00001 以内的数字,则脚本会将 mf_pband.dat 的整行打印到缓存文件中。

我写了一个工作 for 循环来完成工作,但速度非常慢:

kmax=207
bandmin=$(cat bandstructure.dat | awk 'NR==3''{ print }')
bandmax=$(tac bandstructure.dat | awk 'NR==1''{ print }')
nband=$(($bandmax-$bandmin+1))
nheader=46


for ((i=3;i<=$(($kmax*$nband+2)); i++)); do
    kn=$(awk -v i=$i 'NR==i''{ print }'  temp_bandstructure.dat)
    emf=$(awk -v i=$i 'NR==i''{ print }'  temp_bandstructure.dat)
    
    for ((j=$(($nheader+1));j<=$(($kmax*$nband+$nheader)); j++)); do
        kn_mf_pband=$(awk -v j=$j 'NR==j''{ print }'  mf_pband.dat)
        emf_mf_pband=$(awk -v j=$j 'NR==j''{ print }'  mf_pband.dat)
        if [ "$kn" = "$kn_mf_pband" ] && (( $(echo "$emf - $emf_mf_pband <= 0.00001" |bc -l) )) && (( $(echo "$emf_mf_pband - $emf <= 0.00001" |bc -l) ))
        then
            awk -v j=$j 'NR==j' mf_pband.dat >> temp_copying_cache.dat
            echo $i $j $kn $kn_mf_pband $emf $emf_mf_pband
            break
        fi
    done
done

现在我正在尝试将其中一个 for 循环发送到后台任务,这样我就可以 运行 并行处理其中的许多任务。修改后的代码没有报错,也没有任何进展:

task(){
    kn_mf_pband=$(awk -v j=$j 'NR==j''{ print }'  mf_pband.dat)
    emf_mf_pband=$(awk -v j=$j 'NR==j''{ print }'  mf_pband.dat)
    if [ "$kn" = "$kn_mf_pband" ] && (( $(echo "$emf - $emf_mf_pband <= 0.00001" |bc -l) )) && (( $(echo "$emf_mf_pband - $emf <= 0.00001" |bc -l) ))
    then
        awk -v j=$j 'NR==j' mf_pband.dat >> temp_copying_cache.dat
        echo $i $j $kn $kn_mf_pband $emf $emf_mf_pband
    fi
}


for ((i=3;i<=$(($kmax*$nband+2)); i++)); do
    kn=$(awk -v i=$i 'NR==i''{ print }'  temp_bandstructure.dat)
    emf=$(awk -v i=$i 'NR==i''{ print }'  temp_bandstructure.dat)
    
    for j in {$(($nheader+1))..$(($kmax*$nband+$nheader))}; do
        ((i=i%20)); ((i++==0)) && wait
        task "$j" &
    done
done
wait

谁能告诉我为什么任务不是 运行ning,更重要的是,我怎样才能让它们正确地 运行?

问题在

for j in {$(($nheader+1))..$(($kmax*$nband+$nheader))}; do
    ((i=i%20)); ((i++==0)) && wait
    task "$j" &
done

在这里,大括号扩展 {$(($nheader+1))..$(($kmax*$nband+$nheader))} 不是 扩展为数字列表,而是扩展为文字字符串 {47..1234}([= 的实际数字14=] 取决于您的文件内容)。
然后你开始 task '{47..1234}' & 什么都不做,因为在 task 中你试图用 awk -v j='{47..1234}' 'NR==j' 提取值,但 NR 永远不会是 {47..1234}.
要解决此问题,请使用 seqfor ((...; ...; ...))。参见 How do I iterate over a range of numbers defined by variables in Bash?

无论如何,您的脚本很慢,因为您一遍又一遍地读取同一个文件(并且因为您启动了很多进程)。 for i ...; do awk -v i=$i 'NR==i'; done 具有二次时间复杂度。您可以尝试重写 awk 中的脚本,只是为了让它更快。首先,将其中一个文件读入数组并保存在内存中,然后处理另一个文件。

这是这样一个 awk 脚本的框架。成语 FNR==NR 只有在处理第一个文件时才成立。

awk -v bandmax="$(tail -n1 bandstructure.dat | awk '{print }')" -v nheader=46 '
  FNR==NR && NR>nheader { kn_mf_pband[NR-nheader]=; em_mf_pband[NR-nheader]= }
  FNR==NR { next }
  # because of the `next` the following rules are only processed for the 2nd file
  FNR==3 { bandmin= }
  FNR>2 {
    # here you can use for loops to iterate over the stored values in
    # kn_mf_pband[...] and em_mf_pband[...]
  }
' mf_pband.dat bandstructure.dat