使用正则表达式和变量的 ANDed 条件

bev*_*bev 3 bash ksh zsh regular-expression

我想测试从文件中读入的一行是否具有特定的开头和结尾,其中包含保存在变量中的单词。这是一些代码:

输入文件是:

line one
#; line two
#; line three blah
line four
Run Code Online (Sandbox Code Playgroud)

失败的最小脚本是:

declare ENDOFLINE= "blah"
exec 3< "inputfile"

while read LINE <&3
do
    if [[ ("$LINE" =~ "^#;") && (( ("$LINE" =~ "${ENDOFLINE%$}") )) ]]; 
    then
    echo score!
    else echo no score
    fi
done
Run Code Online (Sandbox Code Playgroud)

但是,如果我这样做:

if [[ ("$LINE" =~ "^#;") && (( ("$LINE" =~ "blah$") )) ]];
Run Code Online (Sandbox Code Playgroud)

它成功识别了正确的行(=> #;第三行等等)。换句话说,我需要一个复合测试条件,其中第一个测试是行首是否为“#;” 该行的结尾是包含在变量 $ENDOFLINE 中的字符串。

谢谢你的帮助。

Gil*_*il' 6

看起来像一个简单的错误:在 的末尾${ENDOFLINE%$}去掉 a ,但您想要做的是按字面意思使用并在它后面加上 a来表示行的结尾。$$ENDOFLINE$ENDOFLINE$

if [[ ("$LINE" =~ "^#;") && (( ("$LINE" =~ "${ENDOFLINE}$") )) ]];
Run Code Online (Sandbox Code Playgroud)

这在 zsh 中有效,但在 ksh 或 bash 中无效。Bash 要求 regexp$不加引号(否则,它会按字面解释),并且 ksh 不喜欢里面的双括号[[ … ]](它将它们解释为算术指令)。这条更简单的行适用于所有三个 shell(请注意,这[[…]]是您不需要"…"围绕变量扩展的少数几个地方之一):

if [[ $LINE =~ ^"#;" && $LINE =~ $ENDOFLINE$ ]];
Run Code Online (Sandbox Code Playgroud)

如果$ENDOFLINE从不与#;(例如,如果ENDOFLINE;foo,您将接受#;;foo但拒绝#;foo),您可以将其简化为单个测试:

if [[ $LINE =~ ^"#;".*$ENDOFLINE$ ]];
Run Code Online (Sandbox Code Playgroud)

您还可以在此处使用通配符模式而不是正则表达式:

if [[ $LINE = "#;"*"$ENDOFLINE" ]];
Run Code Online (Sandbox Code Playgroud)

[[…]]构造并不存在于所有 shell 中,它特定于 bash、ksh 和 zsh。在其他 shell(Bourne、dash、任何 POSIX)上,您可以使用以下case结构进行通配符匹配:

case $LINE in
  "#;"*"$ENDOFLINE") echo score;;
  *) echo no score;;
esac
Run Code Online (Sandbox Code Playgroud)