Awk 如何在Unix中从文件名中提取具有已知结构的子字符串?

Awk 如何在Unix中从文件名中提取具有已知结构的子字符串?,awk,grep,Awk,Grep,我有一堆复杂的文件名,其中包含很多信息,我试图从每个文件名中提取两个子字符串 名称具有以下结构: ADNI_002_S_0295_MR_MT1__N3m_Br_20110623105302806_S110476_I241350_RightHippoSubfields.mgz.txt ADNI_002_S_1155_MR_MT1__GradWarp__N3m_Br_20120322164018368_S97230_I291880_RightHippoSubfields.mgz.txt ADNI_0

我有一堆复杂的文件名,其中包含很多信息,我试图从每个文件名中提取两个子字符串

名称具有以下结构:

ADNI_002_S_0295_MR_MT1__N3m_Br_20110623105302806_S110476_I241350_RightHippoSubfields.mgz.txt
ADNI_002_S_1155_MR_MT1__GradWarp__N3m_Br_20120322164018368_S97230_I291880_RightHippoSubfields.mgz.txt
ADNI_002_S_0729_MR_MT1__N3m_Br_20120913163818876_S159861_I334105_RightHippoSubfields.mgz.txt
从每个文件名中,我想在Unix shell中提取:

  • 格式为
    ?\u S\u???
    的字符串(如
    002\u S\u 0295
    002\u\u 115
    002\u 0729
  • 以及括在
    \u I
    和下一个下划线之间的数字(如
    241350
    291880
    334105

  • 我尝试过grep和awk的一些组合,但实际上我无法想出解决方案。

    如果将文件名存储在文件
    文件中,您可以执行以下操作:

    1.-形式为???S???的字符串????(如002__0295、002__115、002__0729)


    对于任何支持ERE的sed:

    $ sed -E 's/.*_([^_]+_S_[^_]+).*_I([^_]+).*/\1 \2/' file
    002_S_0295 241350
    002_S_1155 291880
    002_S_0729 334105
    
    对于任何POSIX sed:

    $ sed 's/.*_\([^_]*_S_[^_]*\).*_I\([^_]*\).*/\1 \2/' file
    002_S_0295 241350
    002_S_1155 291880
    002_S_0729 334105
    
    使用GNU awk匹配第三个参数()


    您可以在Bash中使用正则表达式(假设它是您的shell)来执行以下操作:

    while read -r line || [[ -n $line ]]; do 
        printf "'%s'\n" "$line"
        if [[ "$line" =~ (.{3}_S_.{4}) ]]
        then 
            echo ${BASH_REMATCH[1]}
        fi  
        if [[ "$line" =~ _I([0-9]+) ]]
        then
            echo ${BASH_REMATCH[1]}
        fi  
        echo
    done <file
    

    这可以通过Bash中的扩展模式匹配来实现——诚然,这有点复杂:

    shopt -s extglob
    
    patterns=('???_S_????' '_I+([!_])')
    
    for fname in *.mgz.txt; do
        for pat in "${patterns[@]}"; do
            var=${fname#${fname%$pat*}}
            var=${var%${var##$pat}}
            echo "${var#_I}"
        done
    done
    
    这将使用嵌套的参数扩展来删除部分文件名。第一个文件和第一个模式的示例:

    • 删除模式之前的部分文件名:

      • ${fname%$pat*}
        扩展为
        ${fname%?\u S_???*}
        ,因此它删除了从模式到名称末尾的所有内容,从而产生
        ADNI
      • 现在在
        ${fname#${fname%$pat*}
        中重新使用该结果,它将成为
        ${fname#ADNI}
        ,扩展为

        002_S_0295_MR_MT1__N3m_Br_20110623105302806_S110476_i24135; u rightshipposubfields.mgz.txt
        
        因此,
        var
        现在具有文件名中以模式开头的部分

    • 正在删除模式后的部分文件名:

      • ${var###$pat}
        扩展为
        ${var#####S#}
        ,从文件名的开头删除模式<第一个模式不需要code>##
    (最长匹配),但第二个模式需要1:
    +([!])
    是“一个或多个非下划线字符”,我们需要最长匹配。这种扩展的结果是

    6_S110476_I241350_rightshipposubfields.mgz.txt
    
    i、 例如,
    var
    中我们要删除的部分

  • ${var%${var###$pat}
    展开为

    ${var%6_S110476_I241350_rightshipposubfields.mgz.txt}
    
    这将删除模式之后的所有内容

  • 打印结果:对于第一个模式,这就是它,我们可以直接打印第二个扩展,但是第二个模式此时仍然包含
    \u I
    ,因此我们使用

    echo“${var#u I}”
    
    要移除它。对于第一个模式,这是一个no-op2,对于第二个模式,它删除了
    \u I

  • 这一切的结果是

    002\u S\u 0295
    241350
    002_S_0729
    334105
    002_S_1155
    291880
    

    1
    +()
    模式也是需要
    extglob
    的原因


    2如果
    ???\u S_???
    恰好与以
    \u I
    开头的字符串匹配,则这将导致不必要的删除,但基于示例文件名,则不会。请准确显示所需的输出。您希望每个名称的两部分包含在两个变量中,还是一个字符串中?用一个字符分隔各个部分?请阅读关于如何创建MCVE()——其中一个关键部分是所需的输出。另一个关键部分是展示你的最大努力。对不起,我以为我已经包含了所有必需的元素,但我错了。谢谢你的建议,我以后再问的时候会更仔细、更彻底。在这个问题上补充缺失的信息还不算太晚。@fedorqui:这正是我想要的。非常感谢!您应该提到,grep的
    -P
    选项仅为GNU,并且用手册页上的话来说,
    具有高度的实验性
    ,因此使用它是非常困难的。即使它起作用,由于PCRE的评估方式,它也会非常缓慢。就我个人而言,我会避免这样做。
    $ awk 'match($0,/([^_]+_S_[^_]+).*_I([^_]+)/,a) { print a[1], a[2] }' file
    002_S_0295 241350
    002_S_1155 291880
    002_S_0729 334105
    
    while read -r line || [[ -n $line ]]; do 
        printf "'%s'\n" "$line"
        if [[ "$line" =~ (.{3}_S_.{4}) ]]
        then 
            echo ${BASH_REMATCH[1]}
        fi  
        if [[ "$line" =~ _I([0-9]+) ]]
        then
            echo ${BASH_REMATCH[1]}
        fi  
        echo
    done <file
    
    'ADNI_002_S_0295_MR_MT1__N3m_Br_20110623105302806_S110476_I241350_RightHippoSubfields.mgz.txt'
    002_S_0295
    241350
    
    'ADNI_002_S_1155_MR_MT1__GradWarp__N3m_Br_20120322164018368_S97230_I291880_RightHippoSubfields.mgz.txt'
    002_S_1155
    291880
    
    'ADNI_002_S_0729_MR_MT1__N3m_Br_20120913163818876_S159861_I334105_RightHippoSubfields.mgz.txt'
    002_S_0729
    334105
    
    awk -F'[_I]' '{print $3,$4,$5" "$(NF-1)}' OFS=_ file
    
    002_S_0295 241350
    002_S_1155 291880
    002_S_0729 334105
    
    shopt -s extglob
    
    patterns=('???_S_????' '_I+([!_])')
    
    for fname in *.mgz.txt; do
        for pat in "${patterns[@]}"; do
            var=${fname#${fname%$pat*}}
            var=${var%${var##$pat}}
            echo "${var#_I}"
        done
    done