使用If-Then-Else将文件拆分为3个文件

时间:2019-02-13 19:57:45

标签: shell perl ksh

为什么要分割以Perl编写的大文件的代码运行得比用Korn Shell编写的代码快得多。超过一百万的输入记录。每个记录的前9个字符用于确定记录要写入哪个文件,它在Perl中运行大约4-5分钟。 我试图将这段代码转换为ksh,它似乎可以永远运行(小时)。

我真的不知道我做错了什么导致了这个问题。在某些记录中,字符串中嵌入了空白和/或字母字符,因此比较必须是字符串类型比较。关于使我的ksh脚本获得perl性能的任何想法,或者为什么没有呢?

我尝试了几种不同的选项,因为ksh / bash在处理变量和比较时往往有很多方法可以做相同或相似的事情。对于这个非常老的Perl代码的运行方式,我也没有一个很好的了解。

我的Perl代码:

open(FILEIN,"base.dat") || die "Could not open FILEIN\n.";

open(FILEOUT1,">base1.dat") || die "Could not open FILEOUT1\n.";
open(FILEOUT2,">base2.dat") || die "Could not open FILEOUT2\n.";
open(FILEOUT3,">base3.dat") || die "Could not open FILEOUT3\n.";

$v_break =  "518000000";
$v_break2 = "525000000";

#Run until end of file
while (<FILEIN>)   {
  $v_pcn = substr($_, 0, 9);

  if ($v_break gt $v_pcn) {
     print FILEOUT1 $_;
  }
  elsif (($v_pcn ge $v_break) && ($v_pcn lt $v_break2)) {
     print FILEOUT2 $_;
  }
  else
  {
    print FILEOUT3 $_;
  }
}  #(<FILEIN>)

close(FILEIN);
close(FILEOUT1);
close(FILEOUT2);
close(FILEOUT3);

我的Shell脚本(ksh):

while read inrec                           # Read base file until EOF
 do                                        # Start work loop
    v_pcn=${inrec:0:9}                     # Get 1st 9 Characters in v_pcn
#   v_pcn=${v_pcn/' '/0}                   # Replace blanks with '0'
    if [[ $v_pcn < '518000000' ]]; then    # pcn < "518000000"
         echo $inrec >> base1.dat          # write rec to "base1.dat"
    elif [[ $v_pcn > '525000000' || $v_pcn == '525000000' ]]; then  # pcn >= "525000000"
         echo $inrec >> base3.dat          # write rec to "base3.dat"
    else                                   # else >= "518000000" & < "525000000"
         echo $inrec >> base2.dat          # write rec to "base2.dat"
    fi
 done < base.dat

我希望shell脚本产生3个输出文件,这些输出文件与perl代码产生的输出文件相匹配,并且时间大约相同。

输入:

-rw-r--r--. 1 mfadjobt mfadset 2095795750 Feb 13 10:07 base.dat

输出:

-rw-r--r--. 1 mfadjobt mfadset  461650125 Feb 13 10:07 base1.dat
-rw-r--r--. 1 mfadjobt mfadset  519783625 Feb 13 10:07 base2.dat
-rw-r--r--. 1 mfadjobt mfadset 1114362000 Feb 13 10:07 base3.dat

4 个答案:

答案 0 :(得分:3)

每次使用>> filename时,您都在重新打开文件,将指针移到文件末尾,然后在语句末尾再次关闭文件。最好保持文件打开。

while read inrec                           # Read base file until EOF
 do                                        # Start work loop
    v_pcn=${inrec:0:9}                     # Get 1st 9 Characters in v_pcn
#   v_pcn=${v_pcn/' '/0}                   # Replace blanks with '0'
    if [[ $v_pcn < '518000000' ]]; then    # pcn < "518000000"
         echo $inrec >&3
    elif [[ $v_pcn > '525000000' || $v_pcn == '525000000' ]]; then  # pcn >= "525000000"
         echo $inrec >&4
    else                                   # else >= "518000000" & < "525000000"
         echo $inrec >&5
    fi
 done < base.dat 3>> base1.dat 4>> base2.dat 5>> base3.dat

这将一次打开文件,将其指针保持在文件中,并应极大地加快处理速度。

通常,当shell变慢时,这是由于您正在运行的命令引起的,但是这里没有任何东西产生子shell,所以接下来我看下一个最可能的罪魁祸首-文件处理。这就是我在这里看到的。

答案 1 :(得分:2)

Perl代码被编译为“二进制”表示形式。然后,由高度优化的解释器执行该二进制表示。

另一方面,Shell脚本

  • 每次执行时解析每一行,
  • 文件重定向在每次执行时都会重复一次,
  • 通常执行 external 命令,除非该命令恰好是内置的Shell。

我不确定Korn shell具有哪些内置功能,但是bash有很多内置功能。<​​/ p>

执行外部命令很昂贵 ,因为它至少涉及一个#loading data using generator with class mode = categorical test_datagen = ImageDataGenerator(rescale = 1./255) test_set = test_datagen.flow_from_directory('animals/valid/', target_size=(150,150),class_mode='categorical',batch_size=32) #compile the model with categorical cross entropy model.compile(loss='categorical_crossentropy',optimizer=Adam(lr=0.00001),metrics=['accuracy']) #calculate confusion matrix test_im, test_lbl = next(test_set) predections = model.predict(test_im) predections = np.argmax(predections, axis = 1) test_lbl = np.argmax(test_lbl, axis = 1) conf_mat = confusion_matrix(all_labels, all_predications) if (data.status === 'success') { if ( $.fn.DataTable.isDataTable( '#report_list' ) ) { $('#report_list').destroy(); } [...] 系统调用。

通常,shell脚本仅在极短的情况下(即,当Perl编译器的启动成本高于实际代码执行时间时)才比Perl脚本快。

简短的答案:当您将Shell脚本转换为等效的Perl脚本,它将运行得更快时,请不要感到惊讶。

答案 2 :(得分:0)

为避免与文件描述符混淆,可以使用for循环。

<Tag>${MyArray[0]}</Tag>

答案 3 :(得分:0)

仅在bash上进行了测试,但是下一个解决方案也应在某些ksh版本中使用。 首先重新考虑边界。漂亮的圆形图形使我们可以查看前三个字符。这将使您受益于所有解决方案。 使用tee,您可以写入stdout和文件,也可以写入不同的进程。

tee < base.dat \
    >(grep -E "^([0-4]|50|51[0-7])"    > base1.dat) \
    >(grep -E "^5(1[89]|2[0-4])"       > base2.dat) |
      grep -E "^(52[5-9]|5[3-9]|6-9])" > base3.dat