我有一堆包含大量信息的复杂文件名,我试图从每个文件名中提取两个子字符串。
名称具有以下结构:
???_S_????
从每个文件名中,我想在Unix shell中提取:
002_S_0295
形式的字符串(例如002_S_115
,002_S_0729
,_I
)241350
与下一个下划线之间的数字(如291880
,334105
,{{1}})我尝试了一些grep和awk的组合,但实际上我无法提出解决方案。
答案 0 :(得分:2)
任何支持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为第3个arg匹配():
$ awk 'match($0,/([^_]+_S_[^_]+).*_I([^_]+)/,a) { print a[1], a[2] }' file
002_S_0295 241350
002_S_1155 291880
002_S_0729 334105
答案 1 :(得分:0)
如果您将文件名存储在文件file
中,则可以执行以下操作:
1.-形式的字符串??? S ???? (如002_S_0295,002_S_115,002_S_0729)
$ grep -Po '(?<=_)....S.....(?=_)' file
002_S_0295
002_S_1155
002_S_0729
这会提取_
和_
之间的所有字符,其格式为???? S ????? (注4?S5 ?,而你说3?S4?)。
2.-和_I与下一个下划线之间的数字(如241350,291880,334105)
同样,请使用grep外观:
$ grep -Po '(?<=_I)\d+' file
241350
291880
334105
答案 2 :(得分:0)
您可以在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
打印:
'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
答案 3 :(得分:0)
awk -F'[_I]' '{print $3,$4,$5" "$(NF-1)}' OFS=_ file
002_S_0295 241350
002_S_1155 291880
002_S_0729 334105
答案 4 :(得分:0)
可以通过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%???_S_????*}
,因此会删除从模式到名称末尾的所有内容,从而生成ADNI_
。此结果现在在${fname#${fname%$pat*}}
中重复使用,后者变为${fname#ADNI_}
,扩展为
002_S_0295_MR_MT1__N3m_Br_20110623105302806_S110476_I241350_RightHippoSubfields.mgz.txt
所以var
现在具有以模式开头的文件名部分。
删除模式后的部分文件名:
${var##$pat}
扩展为${var##???_S_????}
,从文件名的开头删除模式。第一个模式不需要##
(最长匹配),但第二个模式不需要{sup> 1 :+([!_])
是“一个或多个非下划线字符”,我们想要的最长匹配。这种扩张的结果是
6_S110476_I241350_RightHippoSubfields.mgz.txt
,即我们想删除的var
部分。
${var%${var##$pat}}
扩展为
${var%6_S110476_I241350_RightHippoSubfields.mgz.txt}
删除模式后的所有内容。
打印结果:对于第一个模式,就是这个,我们可以直接打印第二个扩展,但第二个模式此时仍然包含_I
,所以我们使用
echo "${var#_I}"
删除它。对于第一个模式,这是一个no-op 2 ,对于第二个模式,它删除了_I
。
所有这些的输出是
002_S_0295
241350
002_S_0729
334105
002_S_1155
291880
1 +()
模式也是extglob
所需的原因。
2 如果???_S_????
恰好与 以_I
开头的字符串匹配,那么这会导致不必要的删除,但是在示例文件名上,它不会。