是否可以从第一列包含与其他文件名称匹配的标识符的文件中提取某些行?

Is it possible to extract certain rows from a file where the first column contains an identifier that matches the names of other files?

我昨天在一个更大的问题中提出了这个问题。评论者建议将其拆分为自己的问题。

我有 x 个文件。前五个文件的内容如下所示。在第二行,i 是一个计数器,因此第一个文件的 i = 0i 旁边是 time,当 i 增加 1 时它总是增加 0.5。

但是,我只保留了第x个文件。因此,虽然我拥有的第一个文件 i = 0,第二个文件 i = 100,依此类推。

6 # This file is called "0.xyz" (<--the "6" is the same in all files)
i =       0, time =        0.000, k =      9000000000000
X        -7.6415350292        6.0494971539        8.1919697993
Y        -6.6418362233        5.9231018862        8.4056822626
Y        -8.0518670684        6.3158684817        9.0061271154
X        26.8252967820       20.4661074967       17.8025744066
Y        26.4477411207       20.4071029058       16.9121571912
Y        26.4399648474       21.2950722068       18.1009273227

6 # This file is called "100.xyz"
i =       100, time =        50.000, k =      2500000000000
X        -6.2423192714       -1.5704681396       -9.5648670474
Y        -5.4925100813       -1.6522059045       -8.9030589772
Y        -6.7765278574       -2.3616512405       -9.4776648590
X         4.1248924594       27.8487302083      -17.5400886312
Y         4.1238657681       26.9869907778      -17.9727402579
Y         5.0750649402       28.1292768156      -17.6848507559

6 # This file is called "200.xyz"
i =       200, time =        100.000, k =      3945000000000
X        19.0090162215       -5.9338939011        6.1931167954
Y        18.4748060757       -6.4905073540        5.6656446036
Y        19.2825591449       -6.4479943255        7.0179774953
X        11.0203415273       34.6029396705        2.7220660957
Y        11.1184002007       34.8398120338        1.8089008500
Y        10.3349649622       33.9509485292        2.5605794622

6 # This file is called "300.xyz"
i =       300, time =       150.000, k =      2341000000000
X        -7.6415350292        6.0494971539        8.1919697993
Y        -6.6418362233        5.9231018862        8.4056822626
Y        -8.0518670684        6.3158684817        9.0061271154
X        26.8252967820       20.4661074967       17.8025744066
Y        26.4477411207       20.4071029058       16.9121571912
Y        26.4399648474       21.2950722068       18.1009273227

6 # This file is called "400.xyz"
i =       400, time =        200.000, k =      2500000000000
X        -6.2423192714       -1.5704681396       -9.5648670474
Y        -5.4925100813       -1.6522059045       -8.9030589772
Y        -6.7765278574       -2.3616512405       -9.4776648590
X         4.1248924594       27.8487302083      -17.5400886312
Y         4.1238657681       26.9869907778      -17.9727402579
Y         5.0750649402       28.1292768156      -17.6848507559

我想做的是将这些文件(上方)与另一个文件(下方)的数据进行匹配。在下面的文件中,每一行根据i(步骤)匹配上面的一个文件。然后我想将下面文件中匹配行的前三列打印到一个新文件中。

虽然我包含了文件名,但我更愿意使用 i 而不是文件名来进行匹配。

我知道如何通过简单的迭代来做到这一点。意思是,我可以计数并将下面文件的第 x 行打印到一个新文件中。但是,我想使用一种专门匹配 i 的更复杂的方法,因为这些文件非常长,并且可能缺少行,因此我最终会在上面的文件和我的文件之间不匹配想要这里

    #   Step       Time        Ax                   Ay                  Az                  Bx                  By                  Bz                  Cx                  Cy                  Cz                     Final 
           0       0.000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290          3243.9033438318
           1       0.500       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502          3243.7307919244
           2       1.000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814          3243.5615395313
           3       1.500       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604          3243.3955453870
           4       2.000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354          3243.2327751298
...
           100       50.000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290          3243.9033438318
...
           200       100.000       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502          3243.7307919244
...
           300       150.000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814          3243.5615395313
           301       150.500       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604          3243.3955453870
...
           400       200.000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354          3243.2327751298

示例 我希望通过操作上面的文件来匹配问题顶部的示例文件集的结果:

   0         0.000         14.8032123290        
   100       50.000        14.8032123290
   200       100.000       14.8029498502
   300       150.000       14.8026923814
   400       200.000       14.8021922354

如果有人对如何处理此问题有任何提示,我将不胜感激。

您可以使用 awk 脚本来执行此操作,如下所示:

awk 'FNR == 1 {
if ([=10=] ~ /^i =/) {
    dataFile = 0;
    step[+0] = FILENAME;
}
else {dataFile = 1;}
}

dataFile == 1 && step[] {
    print , , ;
}' *.xyz data.txt

(假设最终文件名为 data.txt;根据需要更改)

FNR == 1 匹配每个文件的第一行,并且将从 xyz 文件中捕获步骤,或者设置一个标志表明我们已经到达数据文件。由于执行数学运算的请求, + 0 位将强制 awk 将第 3 个字段转换为数字(,删除尾随逗号) .

dataFile == 1 && step[$i] 匹配数据文件中在 xyz 文件中看到步长值的行。

注意:您必须在最终数据文件之前指定所有xyz文件,以便在处理数据文件之前收集所有步骤。


抱歉,当我将上述解决方案放在一起时,我认为 # This file is called 行不是文件的一部分。修改后的脚本在这里:

awk '
FNR == 2 && FILENAME != ARGV[ARGC-1] {
    step[+0] = FILENAME;
}

FILENAME == ARGV[ARGC-1] && step[] {
    print , , ;
}' *.xyz data.txt

此版本使用 ARGV 和 ARGC 来确定是否正在处理“数据”文件。

  • 如果不是数据文件和行号== 2,缓存“step”值
  • 如果是数据文件,并且步骤在列表中,打印前3个字段

结果:

0 0.000 14.8032123290
100 50.000 14.8032123290
200 100.000 14.8029498502
300 150.000 14.8026923814
400 200.000 14.8021922354

假设:

  • 匹配基于 i(又名 step)和 time

step / time / Ax数据文件:

$ cat match.dat
    #   Step       Time        Ax                   Ay                  Az                  Bx                  By                  Bz                  Cx                  Cy                  Cz                     Final
           0       0.000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290          3243.9033438318
           1       0.500       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502          3243.7307919244
           2       1.000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814          3243.5615395313
           3       1.500       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604          3243.3955453870
           4       2.000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354          3243.2327751298
           100       50.000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290          3243.9033438318
           200       100.000       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502          3243.7307919244
           300       150.000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814          3243.5615395313
           301       150.500       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604          3243.3955453870
           400       200.000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354          3243.2327751298

一个awk想法:

awk '
FNR==NR   { if (FNR>1)                        # skip header line in 1st file
               Ax[ OFS ]=               # use step + OFS + time as index for Ax[] array
            next
          }

 == "i" { gsub(/,/,"")                      # remove commas from line so we can use normal FS delimiter to pull ...
            i=                              # field #3 (i) and ...
            time=                           # field #6 (time)
            if ( (i OFS time) in Ax)          # if i + OFS + time is an index in Ax[] array ...
                print i,time,Ax[i OFS time]   # print our 3 values to stdout
          }
' match.dat [0-9]*.xyz

这会生成:

0 0.000 14.8032123290
100 50.000 14.8032123290
200 100.000 14.8029498502
300 150.000 14.8026923814
400 200.000 14.8021922354

如果 OP 需要显示带有漂亮列的输出,一个想法是将结果通过管道传输到 column,例如:

$ awk '...' match.dat [0-9]*.xyz | column -t
0    0.000    14.8032123290
100  50.000   14.8032123290
200  100.000  14.8029498502
300  150.000  14.8026923814
400  200.000  14.8021922354

注意:此代码匹配字符串的精确匹配; 不是根据数值匹配;所以 150 != 150.00

这可能适合您 (GNU sed):

sed -En '2~100s/^((\s*\S+){3}).*//p' file

打开扩展正则表达式并关闭隐式打印 -En

从第 2 行开始(header 之后的行)使用模数 100 到 select 所需的行,然后使用替换命令仅保留前三列。

如果必须使用初始文件进行匹配,则:

sed -En 's/^i =\s*(\S+),.*/s#^\(\s\*\(\s\*\S+\)\{2\}\)\.\*#\1#p/p' file1of5 |
sed -Enf - fileWithStep

这会生成一组 sed 命令,这些命令提取步骤值并将它们与具有步骤值的文件进行匹配,并仅检索前 3 列。

N.B。这五个文件可以在第一组 sed 命令后连接或单独命名,即用 file1 file2 file3 file4 file5 替换 file1of5 如果 它们包含唯一的步长值。