在shell脚本中检索正则表达式后的单词

时间:2014-03-20 15:33:58

标签: regex shell

我正在尝试从文本文件中检索具有如下元数据的特定字段:

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',如何在同一行代码中检索正则表达式之后的值?

3 个答案:

答案 0 :(得分:19)

如果提取一个(或者,通常是由不同捕获组捕获的非重复值集合)就足够了,并且您正在运行 { {1}},bashksh ,请考虑使用正则表达式匹配运算符zsh =~:< / p>

向@AdrianFrühwirth提出[[ string =~ regex ]]ksh解决方案要点的提示。

示例输入字符串:

zsh

接下来讨论特定于Shell的string='project=XYZ; cell=ABC; strain=C3H; sex=F; age=PQR; treatment=None; id=MLN' 用法;通过shell函数的=~功能的多shell实现可以在最后找到。


的bash

特殊=~数组变量接收匹配操作的结果:元素BASH_REMATCH包含整个匹配,元素0包含第一个捕获组(带括号的子表达式)匹配,依此类推

1

bash 3.2+

[[ $string =~ \ cell=([^;]+) ]] && cell=${BASH_REMATCH[1]} # -> $cell == 'ABC'
虽然上面的特定命令有效,但在bash bash 4.x中使用正则表达式文字是有缺陷的,特别是在Linux上涉及字边界断言4.x\<时;例如,\>莫名其妙地不匹配;解决方法:使用中间变量(未引用!):[[ a =~ \<a ]]有效(也适用于bash re='\a'; [[ a =~ $re ]])。

3.2+ - 或者在设置bash 3.0 and 3.1之后:
引用正则表达式使其工作:

shopt -s compat31

ksh的

[[ $string =~ ' cell=([^;]+)' ]] && cell=${BASH_REMATCH[1]} # -> $cell == 'ABC' 语法与ksh中的语法相同,除了:

  • 包含匹配字符串的特殊数组变量的名称为bash(即使仅使用.sh.match隐式引用第一个元素,也必须将名称括在{...}中:
${.sh.match}

的zsh

[[ $string =~ \ cell=([^;]+) ]] && cell=${.sh.match[1]} # -> $cell == 'ABC' 语法也类似于bash,除了:

  • 正则表达式文字必须引用 - 为简单起见,或者至少某些 shell元字符,例如{{1} }。
    • 您可以,但无需双引号作为变量值提供的正则表达式。
    • 请注意此引用行为与bash 3.2+的基本不同之处:zsh仅需要出于语法原因引用,并始终将结果字符串作为一个整体视为正则表达式,无论是引用它还是部分引用或不
  • 包含匹配结果的 2 变量:
    • ;包含整个匹配的字符串
    • 数组变量zsh仅包含捕获组的匹配项(请注意$MATCH数组以索引$match开头,并且您不需要在{{{{}}中包含变量名称1}}引用数组元素)
zsh

1运算符的多shell实现,作为shell函数{...}

以下shell函数抽象了 [[ $string =~ ' cell=([^;]+)' ]] && cell=$match[1] # -> $cell == 'ABC' =~reMatchbash运算符之间的差异;匹配在数组变量ksh中返回。

正如@AdrianFrühwirth所指出的那样,要编写便携式(zsh=~${reMatches[@]})代码,您需要在{{1}中执行zsh使其数组以索引 ksh 开头;作为副作用,您还必须在引用数组时使用bash语法,如setopt KSH_ARRAYSzsh)。

应用于我们的示例,我们得到:

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]} 真正使用# 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 } 创建局部变量时需要该函数。

答案 1 :(得分:3)

我不会使用cut,因为您无法指定多个分隔符。

如果您的grep支持PCRE,则可以执行以下操作:

$ string='project=XYZ; cell=ABC; strain=C3H; sex=F; age=PQR; treatment=None; id=MLN'
$ grep -oP '(?<=cell=)[^;]+' <<< "$string"
ABC

您可以使用sed,简单来说可以将其作为 -

$ sed -r 's/.*cell=([^;]+).*/\1/' <<< "$string"
ABC

另一种选择是使用awk。有了它,您可以通过指定要作为字段分隔符考虑的分隔符列表来执行以下操作:

$ awk -F'[;= ]' '{print $5}' <<< "$string"
ABC

你可以通过遍历该行来进行更多检查,这样就不必硬编码来打印第5个字段。

请注意,如果您的shell不支持here-string表示法<<<,那么您可以echo变量并将其传递给命令。

$ echo "$string" | cmd

答案 2 :(得分:2)

这是一个本机shell解决方案:

$ string='project=XYZ; cell=ABC; strain=C3H; sex=F; age=PQR; treatment=None; id=MLN'
$ cell=${string#*cell=}
$ cell=${cell%%;*}
$ echo "${cell}"
ABC

这会从字符串中删除包含cell=的最短前导匹配,然后移除最长的跟踪匹配,直至包含;,并留下ABC

这是另一个使用read分割字符串的解决方案:

$ cat t.sh
#!/bin/bash

while IFS=$'; \t' read -ra attributes; do
    for foo in "${attributes[@]}"; do
        IFS='=' read -r key value <<< "${foo}"
        [ "${key}" = cell ] && echo "${value}"
    done
done <<EOF
foo=X;  cell=ABC;  quux=Z;
foo=X;  cell=DEF;  quux=Z;
EOF

$ ./t.sh
ABC
DEF

对于使用外部工具的解决方案,请参阅@ jaypal的优秀答案。