Bash 用数字匹配多个文件,并按数字排除其中一个文件

Bash 用数字匹配多个文件,并按数字排除其中一个文件,bash,awk,Bash,Awk,我有一系列文件,根据编号(File1.txt、File2.txt、File3.txt等)排序,我在脚本中运行循环作为awk代码的输入。我可以把这些模式匹配为 awk ... file[1-$i].txt >> output 但是,我希望排除该范围内的文件,例如 file$v.txt 目标 我在找像这样的东西 awk ... file[1-!$v-$i].txt >> output 在这里,我从1到$I匹配每个文件,用$v跳过该文件 我已经尝试了前面描述的复合模式匹

我有一系列文件,根据编号(File1.txt、File2.txt、File3.txt等)排序,我在脚本中运行循环作为awk代码的输入。我可以把这些模式匹配为

awk ... file[1-$i].txt >> output
但是,我希望排除该范围内的文件,例如

file$v.txt
目标 我在找像这样的东西

awk ... file[1-!$v-$i].txt >> output
在这里,我从1到$I匹配每个文件,用$v跳过该文件


我已经尝试了前面描述的复合模式匹配的各种输入,但是我无法让语法对我起作用

有人知道如何像这样进行复合模式匹配吗?多谢各位


样本输入 根据要求,以下是我的文件:

文件1.dat

29.078306 0.00676358
29.223592 0.00309192
30.297306 0.0174575
30.478883 0.132458
30.503705 0.118951
30.512891 0.0705088
31.945900 0.00408244
32.321011 0.00258023
32.894037 0.00407912
32.916263 0.00330154
34.594139 0.00874524
34.849178 0.0195172
34.884655 0.00547378
34.967403 0.00308369
35.325397 0.00818193
文件1.2.dat

25.970535 0.0979715
26.913976 0.00593039
29.078306 0.0984052
29.223592 0.00271504
30.236632 0.013818
30.478883 0.0347606
30.503705 0.102369
30.512891 0.0409633
31.714064 0.0242958
31.902306 0.0510168
32.715764 0.0146584
34.952965 0.00484555
35.190790 0.0114201
35.360372 0.0033089
35.575199 0.00282864
38.184618 0.00551692
文件1.3.dat

31.591771 0.0126916
32.059389 0.0605918
32.299959 0.122618
32.890418 0.0058495
32.962536 0.00492958
33.646214 0.0705359
33.679538 0.120592
文件1.4.dat

25.636267 0.00398174
27.848542 0.00485739
28.269278 0.0174401
29.418886 0.00409613
31.313212 0.203932
31.945900 0.00259743
32.256620 0.00325607
32.299959 0.0325366
33.461363 0.0798633
33.646214 0.0516498
33.679538 0.12871
文件1.5.dat

29.767600 0.00777448
32.299959 0.00777995
34.849178 0.0305844
34.884655 0.0126815
34.930799 0.0546924
34.952965 0.0711241
Awk代码 此代码执行以下操作:

  • 将file.4.dat与file.1.dat、file.2.dat匹配。。。file.5.dat,基于字段1($1)中的值
  • 只要在$1中找到匹配项,就会将$2添加到匹配行中的file.4.dat$2中
  • test.out打印file.4.dat,其中$2等于匹配的$1行中$2的总和
  • 我试图做的一个简单的例子是在中被问到的,这就是我的awk代码的来源

    目标 我的目标是在输出中包含以下行:

    33.679538 0.249302
    
    在其他正确匹配的行中,但这行是我当前的测试,以查看它是否工作。现在,我有:

    33.679538 0.378012
    
    由于在awk代码中将file.4.dat添加到自身中,因此我无法在输入文件的第二个参数中排除它

    问题摘要 我的awk代码正在读取我的所有输入文件,我需要排除其中一个文件以获得正确的输出


    最后,我必须分别输入我的5个文件中的每一个,与上面awk代码中的其他4个文件相对照。将来,文件的数量将是可变的,因此我不能只在脚本中键入文件名。现在,如果我能解决这个问题,至少不超过10个文件,这将是一个很大的帮助。

    您可以在
    awk
    中简单地完成这项工作,方法是识别第一个用于参考的文件,并使用
    nextfile
    选项(需要GNU版本)忽略该文件以进行后续处理将跳过处理文件以进行后续处理。按照这种逻辑,您应该将参考文件,例如
    file.4.dat
    作为文件列表中的第一个参数放置在输入中

    awk '
    BEGIN{ ignoreFile = ARGV[1] }
    NR==FNR {
        a[$1]=$2
        next
    }
    FILENAME == ignoreFile { nextfile }
    ($1 in a) {
        a[$1]+=$2
    }
    END {
        for(i in a)
            print i,a[i]
    }' file.4.dat file.[1-5].dat >| test.out
    
    OP想知道他们是否可以构建一个文件名的模式列表,这些文件名可以从shell生成并使用。可以这样做,但考虑到
    nextfile
    中提供的相对简单的选项,这可能看起来很复杂

    根据您的理解,您有
    n
    文件,其中一个将用作参考文件。我更喜欢使用
    bash
    shell的特性来包含除引用之外的所有文件。例如,我正在创建文件
    file1..10
    来解释这一点

    touch file{1..10}
    exclude=3
    
    使用内置的
    shopt
    设置扩展shell选项

    shopt -s extglob
    list=(!(file"$exclude"))
    
    现在使用
    declare-plist
    打印数组,以查看仅排除引用文件的文件列表。现在使用
    awk
    中的数组,如下所示。数组扩展
    “${list[@]}”
    会导致上面生成的所有文件被排除

    awk ... file"$exclude" "${list[@]}"
    

    要跳过文件,只需将
    ARGV[其在arg列表中的位置]
    设置为null。e、 g:

    $ ls
    file1  file2  file3
    
    $ grep . file*
    file1:x
    file2:y
    file3:z
    
    $ awk 'BEGIN{ARGV[2]=""} {print FILENAME, $0}' file*
    file1 x
    file3 z
    
    或者,如果愿意,可以按名称而不是按参数列表中的顺序删除“坏”文件:

    $ awk 'BEGIN{for (i in ARGV) if (ARGV[i]=="file2") ARGV[i]=""} {print FILENAME, $0}' file*
    file1 x
    file3 z
    
    $ awk 'BEGIN{bad["file2"]; for (i in ARGV) if (ARGV[i] in bad) ARGV[i]=""} {print FILENAME, $0}' file*
    file1 x
    file3 z
    
    $ awk '
        BEGIN {
            split("file2 file3",tmp); for (i in tmp) bad[tmp[i]]
            for (i in ARGV) if (ARGV[i] in bad) ARGV[i]=""
        }
        {print FILENAME, $0}
    ' file*
    file1 x
    

    如果有人不想使用或其系统中没有
    nextfile
    ,则以下操作可能会有所帮助

    awk -v ignore="file.4.dat" '
    FNR==1{
        no_parse=""
    }
    FNR==NR {
        a[$1]=$2
        next
    }
    FILENAME == ignore{
        no_parse=1
    }
    no_parse{
        next
    }
    ($1 in a) {
        a[$1]+=$2
    }
    END {
        for(i in a)
            print i,a[i]
    }' file.4.dat file.[1-5].dat >| test.out
    

    创建了一个名为
    ignore
    的变量,我们可以提到需要忽略的名为的输入文件,一旦该输入文件用于解析,我将名为
    no\u parse
    的标志设置为TRUE,在这种情况下,特定输入文件的no内容将被读取(因为
    next
    用于跳过所有进一步的语句)使用流水线AWK。您必须提供最后一个文件作为参考(此处->4)

    使用给定的文件

    $ awk ' $(NF+1)=FILENAME' file.[1-3].dat file.5.dat file.4.dat |  
          awk ' { a[$1]+=$2; $2=a[$1] } /file.4.dat/ && NF-- '
    25.636267 0.00398174
    27.848542 0.00485739
    28.269278 0.0174401
    29.418886 0.00409613
    31.313212 0.203932
    31.945900 0.00667987
    32.256620 0.00325607
    32.299959 0.162935
    33.461363 0.0798633
    33.646214 0.122186
    33.679538 0.249302
    
    $
    

    是的,我们可以用这种方式提到多个输入文件。请您在您的帖子中发布文件样本以及预期输出,然后让我们知道。
    [1-$i]
    不是您可能想到的数字范围——例如,
    [1-30]
    匹配单个数字,
    0
    1
    2
    3
    ,因为它将
    1-3
    0
    视为建立单个字符模式的两个元素。@RavinderSingh13好的,我会的。坦白说,在事实发生后进行过滤可能更有意义:
    shopt-s extglob
    ,然后
    files=();对于file+([[:digit:]]).txt中的文件;do fileNum=${file/[![:digit:][]/};((fileNum
    ,然后您可以
    awk。。。“${files[@]}”
    来传递列表。@Inian,
    |
    作为覆盖
    noclobber
    的一种方法在bash中;请参阅手册的重定向部分。这对我很有效。非常感谢!为了将来读者根据我问题的主题进行搜索,是否可以指定匹配文件名的模式列表,同时从列表中排除一个模式?@Blaisem:是的,您可以构建一个文件数组并排除该文件。为了您愿意知道,我可以明天更新答案!:)@布莱森,请你也检查一下这个,如果有任何疑问请告诉我。
    awk ' $(NF+1)=FILENAME' file.[1-3].dat file.5.dat file.4.dat |  
       awk ' { a[$1]+=$2; $2=a[$1] } /file.4.dat/ && NF-- '
    
    $ awk ' $(NF+1)=FILENAME' file.[1-3].dat file.5.dat file.4.dat |  
          awk ' { a[$1]+=$2; $2=a[$1] } /file.4.dat/ && NF-- '
    25.636267 0.00398174
    27.848542 0.00485739
    28.269278 0.0174401
    29.418886 0.00409613
    31.313212 0.203932
    31.945900 0.00667987
    32.256620 0.00325607
    32.299959 0.162935
    33.461363 0.0798633
    33.646214 0.122186
    33.679538 0.249302
    
    $