使用 bash,根据模式匹配创建文件

使用 bash,根据模式匹配创建文件

我们有一个内容如下的文件。

/* ----------------- pattern_1 ----------------- */ 

jb: pattern_1   Data: asdflj  adfas Log: dir/log1

/* ----------------- pattern_2 ----------------- */ 

jb: pattern_2   typ: CM comm: ${dir}/DISPV mach: au_buh description: "run flag"


/* ----------------- pattern_3 ----------------- */ 

jb: pattern_3   typ: fw  own: buh out_file: "${log}/jl.log" err: "log.err"

该文件必须分成 3 个部分并创建为以下 3 个单独的文件:

模式1.txt

/* ----------------- pattern_1 ----------------- */ 

jb: pattern_1   Data: asdflj  adfas Log: dir/log1

模式2.txt

* ----------------- pattern_2 ----------------- */ 

jb: pattern_2   typ: CM comm: ${dir}/DISPV mach: au_buh description: "run flag"

模式3.txt

/* ----------------- pattern_3 ----------------- */ 

jb: pattern_3   typ: fw  own: buh out_file: "${log}/jl.log" err: "log.err"

尝试了“awk 模式匹配”,没有成功。有什么帮助吗?

答案1

在纯 Bash 中:

while IFS= read -r line; do
  [[ "$line" =~ /\*.*(pattern_[0-9]+).*\*/ ]] && f="${BASH_REMATCH[1]}"
  [[ "$line" =~ ^$ ]] || echo "$line" >> "${f}.txt"
  done < file

...如果您希望在新文件中包含原始空白行,请删除[[ "$line" =~ ^$ ]] ||

注意如果以模式名称命名的文件(即,pattern_N.txt在当前工作目录中预先存在)则它们的输出将被附加到其中,这可能不是您想要的,因此在运行上述循环之前,如果存在这些文件,请先清除这些文件。

使用 AWK(首选 GNU AWK):

awk '
/\/\*.*pattern_[0-9]+.*\*\// {
    r = $0
    gsub("[^pattern_0-9]*", "", r)
    out=r".txt"
    print > out
}

! /\/\*.*pattern_[0-9]+.*\*\/|^$/ {
    print > out
}
' file

...如果您希望在新文件中包含原始空白行,请删除|^$

示范:

$ cat file
/* ----------------- pattern_1 ----------------- */ 

jb: pattern_1   Data: asdflj  adfas Log: dir/log1

/* ----------------- pattern_2 ----------------- */ 

jb: pattern_2   typ: CM comm: ${dir}/DISPV mach: au_buh description: "run flag"


/* ----------------- pattern_3 ----------------- */ 

jb: pattern_3   typ: fw  own: buh out_file: "${log}/jl.log" err: "log.err"
$
$
$ awk '
/\/\*.*pattern_[0-9]+.*\*\// {
    r = $0
    gsub("[^pattern_0-9]*", "", r)
    out=r".txt"
    print > out
}

! /\/\*.*pattern_[0-9]+.*\*\/|^$/ {
    print > out
}
' file
$
$
$ head pattern_*.txt
==> pattern_1.txt <==
/* ----------------- pattern_1 ----------------- */ 
jb: pattern_1   Data: asdflj  adfas Log: dir/log1

==> pattern_2.txt <==
/* ----------------- pattern_2 ----------------- */ 
jb: pattern_2   typ: CM comm: ${dir}/DISPV mach: au_buh description: "run flag"

==> pattern_3.txt <==
/* ----------------- pattern_3 ----------------- */ 
jb: pattern_3   typ: fw  own: buh out_file: "${log}/jl.log" err: "log.err"

答案2

这是另一种 awk 方法:

awk '/----/{ f=$3 }; { print > f }' file 

脚本将检查当前行是否匹配---,如果匹配,则将该行的第三个字段保存为变量f。Awk 将每个输入行按空格拆分为字段(默认情况下),因此如果该行看起来像

/* ----------------- pattern_2 ----------------- */ 

那么,第一个字段是/*,第二个字段是-----------------,第三个字段是pattern_2

接下来,脚本将打印到一个文件中,该文件的名称是f变量 ( print > f) 中存储的内容。所有这些操作的结果是三个名为pattern_1pattern_2和的文件pattern_3,每个文件都有相关内容。

如果您确实希望它们有扩展名(这在 Linux 中是不需要的并且没有帮助,它只是为了让您知道文件类型;我个人从来不使用这样的东西,因为它们只会使我的文件变得杂乱),您只需将最后一个更改print{ print > f".txt" }.

答案3

拆分(请注意,这将创建一个空文件pattern_0.txt,您可以通过添加-z/--elide-empty-files选项来抑制它,但这将导致剩余文件编号“减少”1):

$ csplit -f 'pattern_' -b '%d.txt' file '/\/\* -\{1,\} pattern_[0-9] -\{1,\} \*\//' '{*}'
0
105
136
129

导致

$ head pattern_{1,2,3}.txt 
==> pattern_1.txt <==
/* ----------------- pattern_1 ----------------- */ 

jb: pattern_1   Data: asdflj  adfas Log: dir/log1


==> pattern_2.txt <==
/* ----------------- pattern_2 ----------------- */ 

jb: pattern_2   typ: CM comm: ${dir}/DISPV mach: au_buh description: "run flag"



==> pattern_3.txt <==
/* ----------------- pattern_3 ----------------- */ 

jb: pattern_3   typ: fw  own: buh out_file: "${log}/jl.log" err: "log.err"

根据您的实际使用情况,csplit 正则表达式可以变得更简单 - 例如'/^\/\*/'(“行以/*”开头)。

答案4

以下解释了这些雨天可能发生的情况:

  1. s之间的“模式”---可能实际上不包含单词pattern
  2. pattern_<N>可能包含/s 或空格
  3. pattern_<N>可能在输入中出现多次
  4. 输入中可能有太多唯一的pattern_<N>s,以至于超出了一个进程可以同时打开的最大文件数(这会导致大多数 awk 因“打开的文件太多”错误而失败,或者 GNU awk 的速度显著变慢,因为它需要根据需要在内部管理打开/关闭文件)。

此输入包括前 3 种情况:

$ cat file
/* ----------------- pattern_1 ----------------- */

jb: pattern_1   Data: asdflj  adfas Log: dir/log1

/* ----------------- pattern_2 ----------------- */

jb: pattern_2   typ: CM comm: ${dir}/DISPV mach: au_buh description: "run flag"


/* ----------------- blank: tab:        slash: / ----------------- */

jb: pattern_3   typ: fw  own: buh out_file: "${log}/jl.log" err: "log.err"

/* ----------------- pattern_2 ----------------- */

jb: pattern_2   second appearance of pattern_2

/* ----------------- pattern_1 ----------------- */

jb: pattern_1   second appearance of pattern_1

我们使用装饰-排序-取消装饰习惯用法是将所有具有相同“模式”的块分组在一起,但保留它们的相对输入顺序,然后创建输出文件,因此我们只需要一次打开 1 个输出文件,以避免上述第 4 个问题。

我们将从“模式”映射/\t输出_文件名中 - 按照您喜欢的方式处理它们,除非它们确实可能发生。

以下使用 GNU awk 将第三个参数用作match()\s的简写[[:space:]],并允许使用 NUL 字符\0和 GNU 排序-z

$ cat tst.sh
#!/usr/bin/env bash

decorate() {
    awk -v OFS='\n' '
        match($0,/^\s*\/\*\s+-+\s+(.*)\s+-+\s+\*\/\s*$/,a) {
            printf "%s%s\t%d\t", blockSep, gensub(/[/\t]/,"_","g",a[1]), NR
            blockSep = "\0"
        }
        { print }
        END { printf "%s", blockSep }
    ' "${@:--}"
}

undecorate() {
    awk -F'\t' -v RS='\n\0' '
        $1 != prev {
            close(out)
            out = $1 ".txt"
            prev = $1
        }
        {
            sub(/^([^\t]+\t){2}/,"")
            print > out
        }
    ' "${@:--}"
}

decorate "${@:--}" |
sort -z -k1,1 -k2,2n |
undecorate

$ ./tst.sh file

$ head pattern* blank*
==> pattern_1.txt <==
/* ----------------- pattern_1 ----------------- */

jb: pattern_1   Data: asdflj  adfas Log: dir/log1

/* ----------------- pattern_1 ----------------- */

jb: pattern_1   second appearance of pattern_1


==> pattern_2.txt <==
/* ----------------- pattern_2 ----------------- */

jb: pattern_2   typ: CM comm: ${dir}/DISPV mach: au_buh description: "run flag"


/* ----------------- pattern_2 ----------------- */

jb: pattern_2   second appearance of pattern_2


==> blank: tab:_slash: _.txt <==
/* ----------------- blank: tab:        slash: / ----------------- */

jb: pattern_3   typ: fw  own: buh out_file: "${log}/jl.log" err: "log.err"

我们可以通过一次调用 GNU awk 完成上述所有操作,但使用上述 awk-sort-awk 方法,awk 一次只需在内存中存储 1 个“模式”输入块,而如果我们也在sortgawk 中执行此操作,则它必须一次将整个输入保存在内存中,因此如果输入文件很大,则 YMMV。通过使用 GNUsort来处理排序,它将更好地处理大量文件,因为它旨在使用需求分页等来处理此类输入,而 gawk 则不是。

相关内容