我有一些文件,如下例所示:
2000_A_tim110_may112_AATT_V22_P001_R1_001_V23_P007_R2_001_comb.ext
2000_BB_tim110_may112_AAMM_V14_P002_R1_001_V45_P008_R2_001_comb.ext
2000_C_tim110_DDFF_V18_P006_R1_001.ext
2000_DD_may112_EEJJ_V88_P004_R1_001.ext
从这些文件名中,我想提取前导2000_[A-Z]{1,2}
和所有实例V[0-9]{2}
的正则表达式模式。
也就是说,
来自
2000_A_tim110_may112_AATT_V22_P001_R1_001_V23_P007_R2_001_comb.ext
我想拥有
2000_A_V22_V23
并且来自
2000_DD_may112_EEJJ_V88_P004_R1_001.ext
我想拥有
2000_DD_V88
我一直在尝试通过sed
实现这一目标,但到目前为止我还没有取得任何成功。
起初-天真的-我尝试过
find *.ext | sed -r 's/^(2000_[A-Z]{1,2}).*(V{1}[0-9]{2,3}).*(V{1}[0-9]{2,3}).*\.ext/\1_\2_\3/'
结果是:
2000_A_V22_V23
2000_BB_V14_V45
2000_C_tim110_DDFF_V18_P006_R1_001.ext
2000_DD_may112_EEJJ_V88_P004_R1_001.ext
这不是我想要的,因为这里的两个文件名未编辑返回。
然后,在阅读this post之后,我尝试将在中间捕获的组设为可选,如下所示:
find *.ext | sed -r 's/^(2000_[A-Z]{1,2}).*(V{1}[0-9]{2})?.*(V{1}[0-9]{2}).*\.ext/\1_\2_\3/'
但是自从返回以来,这似乎也不起作用
2000_A__V23
2000_BB__V45
2000_C__V18
2000_DD__V88
(即中间的捕获组似乎已被完全跳过。)
我的问题是,如何获得以下结果?
2000_A_V22_V23
2000_BB_V14_V45
2000_C_V18
2000_DD_V88
我要去哪里错了?或者相反,我想念什么?我是sed
和regex
的新手,而且我想学习很好地使用两者,因此非常感谢指针和指导。
答案 0 :(得分:3)
使用GNU awk进行FPAT:
$ awk -v FPAT='^2000_[A-Z]{1,2}|V[0-9]{2}' '{out=$1; for (i=2; i<=NF;i++) out=out "_" $i; print out}' file
2000_A_V22_V23
2000_BB_V14_V45
2000_C_V18
2000_DD_V88
答案 1 :(得分:1)
您可以将grep
与循环一起使用:
for f in $(find 2000* -regex '2000_[A-Z].*ext'); do
printf "%s\n" $(grep -Eo "^2000_[A-Z]{1,2}|_V[0-9]{2}" <<<"$f" | tr -d "\n")
done
答案 2 :(得分:0)
作为纯bash
解决方案(对不起,没有sed
),该怎么做:
#!/bin/bash
pat='((^2000_[A-Z]{1,2})|(_V[0-9]{2}))(.*)'
while IFS= read -r -d '' line; do
result=
while [[ $line =~ $pat ]]; do
result+="${BASH_REMATCH[1]}"
line="${BASH_REMATCH[4]}"
done
[[ -n "$result" ]] && echo "$result"
done < <(find . -type f -name '*.ext' -printf '%f\0')
输出:
2000_A_V22_V23
2000_BB_V14_V45
2000_C_V18
2000_DD_V88
答案 3 :(得分:0)
基本sed有什么难处?借助sed的替代功能,使用交替|
运算符的功能。
$ cat sedtets
2000_A_tim110_may112_AATT_V22_P001_R1_001_V23_P007_R2_001_comb.ext
2000_BB_tim110_may112_AAMM_V14_P002_R1_001_V45_P008_R2_001_comb.ext
2000_C_tim110_DDFF_V18_P006_R1_001.ext
2000_DD_may112_EEJJ_V88_P004_R1_001.ext
$ sed 's/\(2000_[A-Z]\{1,2\}\|_V[0-9]\+\)\|./\1/g' sedtets
2000_A_V22_V23
2000_BB_V14_V45
2000_C_V18
2000_DD_V88
答案 4 :(得分:0)
正如我在comment中指出的那样,很难在sed
中完成这项工作。但是,只要仔细使用分支和测试,就可以完成。
我使用的是经典的sed
BRE表示法;如果您选择使用更现代但不一定是可移植的ERE表示法,则可以消除大量的反斜杠。我还将脚本保存在文件sed.script
中,并将示例数据保存在文件data
中,并使用以下命令运行命令:
$ sed -f sed.script data
2000_A_V22_V23
2000_BB_V14_V45
2000_C_V18
2000_DD_V88
$
脚本包含:
:retry
s/^\(2000_[A-Z]\{1,2\}\(_V[0-9][0-9]\)*\)_[^_]\{1,\}$/\1/
t
s/^\(2000_[A-Z]\{1,2\}\(_V[0-9][0-9]\)*\)_[^_]\{1,\}_/\1_/
t retry
retry
。s///
会查找2000_
,后跟一个或两个大写字母,然后是一个零个或多个下划线,一个V和两位数字的实例(所有这些都被记住了) );然后是一个下划线和一个或多个非下划线的序列以及行尾。取而代之的是这些。s///
匹配,则分支到脚本的末尾(t
,不带标签名称)。这样就可以打印该行。s///
与第一行非常相似,不同之处在于,它不查找行尾,而是在下划线和非下划线序列之后寻找另一个下划线。请注意,寻找_V##
的术语(其中#
代表数字)会找到尽可能多的术语,因此_xxx_
术语与_V##_
不匹配。取而代之的是记住的术语和下划线,因此它从字符串中删除了_xxx_
的一个单元。s///
匹配,那么它将分支回到脚本的开头。s///
不匹配,则循环将中断并打印剩余内容。实际上,样本数据无法达到此要求,但如果输入行根本不匹配(例如,它以2001
而不是2000
开头),则在不工作后将打印不变通过s///
中的任何一个操作打开。如果应删除与起始模式不匹配的行,则可以通过在脚本开头添加一行来解决:
/^2000_[A-Z]\{1,2\}/!d
如果不包含任何_V##_
序列的行也可以处理,可以在retry
标签之前添加更多行。如果一行的末尾有_V##
(并且很快就没了),那么它将跳过下一行。下一行在行中间查找_V##_
,如果不匹配,则删除该行。
/_V[0-9][0-9]$/b skip
/_V[0-9][0-9]_/!d
:skip
您可以通过在每个p
操作之后添加s///
来查看进度,该操作也显示了中间结果:
2000_A_may112_AATT_V22_P001_R1_001_V23_P007_R2_001_comb.ext
2000_A_AATT_V22_P001_R1_001_V23_P007_R2_001_comb.ext
2000_A_V22_P001_R1_001_V23_P007_R2_001_comb.ext
2000_A_V22_R1_001_V23_P007_R2_001_comb.ext
2000_A_V22_001_V23_P007_R2_001_comb.ext
2000_A_V22_V23_P007_R2_001_comb.ext
2000_A_V22_V23_R2_001_comb.ext
2000_A_V22_V23_001_comb.ext
2000_A_V22_V23_comb.ext
2000_A_V22_V23
2000_A_V22_V23
2000_BB_may112_AAMM_V14_P002_R1_001_V45_P008_R2_001_comb.ext
2000_BB_AAMM_V14_P002_R1_001_V45_P008_R2_001_comb.ext
2000_BB_V14_P002_R1_001_V45_P008_R2_001_comb.ext
2000_BB_V14_R1_001_V45_P008_R2_001_comb.ext
2000_BB_V14_001_V45_P008_R2_001_comb.ext
2000_BB_V14_V45_P008_R2_001_comb.ext
2000_BB_V14_V45_R2_001_comb.ext
2000_BB_V14_V45_001_comb.ext
2000_BB_V14_V45_comb.ext
2000_BB_V14_V45
2000_BB_V14_V45
2000_C_DDFF_V18_P006_R1_001.ext
2000_C_V18_P006_R1_001.ext
2000_C_V18_R1_001.ext
2000_C_V18_001.ext
2000_C_V18
2000_C_V18
2000_DD_EEJJ_V88_P004_R1_001.ext
2000_DD_V88_P004_R1_001.ext
2000_DD_V88_R1_001.ext
2000_DD_V88_001.ext
2000_DD_V88
2000_DD_V88
如果您的sed
支持POSIX sed
所需的扩展,则可以简化脚本。例如,如果可以使用|
或+
,则可能会有一些选项可以简化脚本。这适用于任何版本的sed
。
此代码已经在macOS(BSD)sed
和GNU sed
上进行了测试,并且两者都相同。