我正在尝试从具有元数据的文本文件中检索特定字段,如下所示:
project=XYZ; cell=ABC; strain=C3H; sex=F; age=PQR; treatment=None; id=MLN
我有以下脚本来检索字段
'cell'
while read line
do
cell="$(echo $line | cut -d";" -f7 )"
echo $cell
fi
done < files.txt
但是,以下脚本将整个字段检索为
cell=ABC
,而我只想要值 'ABC'
从字段中,如何在同一行代码中检索正则表达式之后的值?
最佳答案
如 提取一个值 (或者,通常,由不同捕获组捕获的一组非重复值)就足够了,您正在运行 bash
, ksh
, 或 zsh
,考虑使用 正则表达式匹配运算符,=~
:[[ string =~ regex ]]
:
向@Adrian Frühwirth 致敬,了解 ksh
的要点和 zsh
解决方案。
示例输入字符串:
string='project=XYZ; cell=ABC; strain=C3H; sex=F; age=PQR; treatment=None; id=MLN'
Shell特定使用
=~
接下来讨论; =~
的多 shell 实现可以在最后找到通过 shell 函数实现的功能。bash
特价
BASH_REMATCH
数组变量接收匹配操作的结果:元素0
包含整个匹配项,元素 1
第一个捕获组的(带括号的子表达式)匹配,依此类推。bash 3.2+
:[[ $string =~ \ cell=([^;]+) ]] && cell=${BASH_REMATCH[1]} # -> $cell == 'ABC'
bash 4.x
:虽然上面的特定命令有效,但在 bash
4.x
中使用正则表达式文字有问题,特别是在涉及词边界断言时 \<
和 \>
在 Linux 上;例如,[[ a =~ \<a ]]
莫名的不匹配;解决方法:使用中间变量(未加引号!):re='\a'; [[ a =~ $re ]]
作品(也在 bash 3.2+
上)。bash 3.0 and 3.1
- 或设置后 shopt -s compat31
:引用正则表达式以使其工作:
[[ $string =~ ' cell=([^;]+)' ]] && cell=${BASH_REMATCH[1]} # -> $cell == 'ABC'
克什
ksh
语法与 bash
中的相同, 除了:.sh.match
(您必须将名称括在 {...}
中,即使只是用 ${.sh.match}
隐式引用第一个元素): [[ $string =~ \ cell=([^;]+) ]] && cell=${.sh.match[1]} # -> $cell == 'ABC'
zsh
zsh
语法也类似于 bash,除了:;
.zsh
仅出于语法原因需要引用,并且始终将结果字符串作为一个整体视为正则表达式,无论它或其中的一部分是否被引用。 $MATCH
包含整个匹配的字符串 $match
仅包含捕获组的匹配项(注意 zsh
数组以索引 1
开头,并且您不需要将变量名括在 {...}
中以引用数组元素) [[ $string =~ ' cell=([^;]+)' ]] && cell=$match[1] # -> $cell == 'ABC'
=~
的多shell实现运算符作为 shell 函数 reMatch
以下shell函数抽象了
bash
之间的差异, ksh
, zsh
关于 =~
运算符(operator);匹配在数组变量 ${reMatches[@]}
中返回.正如@Adrian Frühwirth 指出的那样,要使用此编写可移植(跨
zsh
、 ksh
、 bash
)代码,您需要执行 setopt KSH_ARRAYS
在 zsh
使其数组以索引0
开头;作为副作用,您还必须使用 ${...[]}
引用数组时的语法,如 ksh
和 bash
)。应用于我们的示例,我们将得到:
# zsh: make arrays behave like in ksh/bash: start at *0*
[[ -n $ZSH_VERSION ]] && setopt KSH_ARRAYS
reMatch "$string" ' cell=([^;]+)' && cell=${reMatches[1]}
shell 函数:
# SYNOPSIS
# reMatch string regex
# DESCRIPTION
# Multi-shell implementation of the =~ regex-matching operator;
# works in: bash, ksh, zsh
#
# Matches STRING against REGEX and returns exit code 0 if they match.
# Additionally, the matched string(s) is returned in array variable ${reMatch[@]},
# which works the same as bash's ${BASH_REMATCH[@]} variable: the overall
# match is stored in the 1st element of ${reMatch[@]}, with matches for
# capture groups (parenthesized subexpressions), if any, stored in the remaining
# array elements.
# NOTE: zsh arrays by default start with index *1*.
# EXAMPLE:
# reMatch 'This AND that.' '^(.+) AND (.+)\.' # -> ${reMatch[@]} == ('This AND that.', 'This', 'that')
function reMatch {
typeset ec
unset -v reMatch # initialize output variable
[[ $1 =~ $2 ]] # perform the regex test
ec=$? # save exit code
if [[ $ec -eq 0 ]]; then # copy result to output variable
[[ -n $BASH_VERSION ]] && reMatch=( "${BASH_REMATCH[@]}" )
[[ -n $KSH_VERSION ]] && reMatch=( "${.sh.match[@]}" )
[[ -n $ZSH_VERSION ]] && reMatch=( "$MATCH" "${match[@]}" )
fi
return $ec
}
笔记:
function reMatch
(与 reMatch()
相反)用于声明函数,这是 ksh
所必需的使用 typeset
真正创建局部变量. 关于regex - 在 shell 脚本中检索正则表达式后的单词,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22537804/