我们有一个内容如下的文件。
/* ----------------- 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_1
、pattern_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
以下解释了这些雨天可能发生的情况:
- s之间的“模式”
---
可能实际上不包含单词pattern
pattern_<N>
可能包含/
s 或空格pattern_<N>
可能在输入中出现多次- 输入中可能有太多唯一的
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 个“模式”输入块,而如果我们也在sort
gawk 中执行此操作,则它必须一次将整个输入保存在内存中,因此如果输入文件很大,则 YMMV。通过使用 GNUsort
来处理排序,它将更好地处理大量文件,因为它旨在使用需求分页等来处理此类输入,而 gawk 则不是。