在 WHILE 中使用 Bash“=~” 运算符而不是 UNTIL 循环

在 WHILE 中使用 Bash“=~” 运算符而不是 UNTIL 循环

我正在编写一些 shell 脚本,要求用户提供域名。我想提示用户循环输入,如果输入不是有效的域名,则写入错误消息。

基本上,代码应如下所示:

#!/bin/bash
 
read -p "Enter domain name: " DOMAIN_NAME

while [[ testing for valid domain name goes here ]]
do
  echo ''
  echo 'You entered an invalid domain name. Please re-enter.'
  echo '... more error message data ....'
  echo ''
  read -p "Enter domain name: " DOMAIN_NAME
done

echo "You entered domain name: $DOMAIN_NAME"

我发现了一些示例,展示了如何使用“=~”运算符来测试正则表达式。然而,这些例子都显示了UNTIL循环。使用问题答案中的正则表达式在 bash 中使用正则表达式检查有效(子)域,一个例子是:

# !/bin/bash

until [[ "$DOMAIN_NAME" =~ ^([a-zA-Z0-9](([a-zA-Z0-9-]){0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,5}$ ]]
do
  read -p "Enter domain name: " DOMAIN_NAME
done

echo "You entered domain name: $DOMAIN_NAME"

虽然这可行,但在用户输入无效数据的情况下编写额外的错误消息并不简单。这样的消息必须写在循环内部,在 之前read,但不是在第一次迭代时。

出于这个原因,我宁愿将循环编写为while如上所示的循环。我无法找到如何否定测试表达式,这是将until循环重写为while循环时所需要的。

答案1

until cmd是相同的while ! cmd

在这里,只需执行以下操作:

until
  IFS= read -rep 'Enter domain name: ' domain
  [[ $domain =~ $regexp ]]
do
  printf>&2 'Invalid domain: "%s"\n' "$domain"
done

printf 'You entered: "%s"\n' "$domain"

与...一样:

while
  IFS= read -rep 'Enter domain name: ' domain
  ! [[ $domain =~ $regexp ]]
do
  printf>&2 'Invalid domain: "%s"\n' "$domain"
done

printf 'You entered: "%s"\n' "$domain"

或者:

while
  IFS= read -rep 'Enter domain name: ' domain
  [[ ! $domain =~ $regexp ]]
do
  printf>&2 'Invalid domain: "%s"\n' "$domain"
done

printf 'You entered: "%s"\n' "$domain"

请注意,我们有/循环read的 in 条件部分,因此每次在正则表达式测试之前都会运行它,但我们不检查其退出状态。它是条件部分(此处或)中运行的最后一个命令,决定循环的分支。whileuntil[[ ... ]]! [[ ... ]]

另请记住,您不能使用字符范围进行输入验证,除非您将语言环境修复为 C 或切换到 zsh 等 shell,其中范围基于代码点。

#! /bin/zsh -
set -o extendedglob
until
  domain=
  vared -p 'Enter domain name: ' domain
  [[ $domain = ([a-zA-Z0-9]([a-zA-Z0-9-](#c0,61)[a-zA-Z0-9]|).)##[a-zA-Z](#c2,5) ]]
do
  printf>&2 'Invalid domain: "%s"\n' "$domain"
done
printf 'You entered: "%s"\n' "$domain"

(这里使用vared(var editor) 内置函数能够像 bash 一样使用 zsh 的行编辑器read -e)。

如果使用=~in 进行正则表达式匹配zsh(而不是与 进行全局模式匹配=),您需要确保通过使用该rematchpcre选项来使用 PCRE。默认情况下,您会像 bash 一样获得 ERE,但有其所有限制。但请记住,PCRE 与 ERE 的等价物$\z。所以:

regexp='^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,5}\z'

请注意,在 UTF-8 语言环境中,未形成有效字符的字节序列将触发 PCRE 错误。使用 zsh glob 模式时可以更优雅地处理这些问题。

在 bash 中,在 C 语言环境之外,如果您只想匹配这 10 个字符(无论启用或不启用该选项),您需要[0123456789]而不是。与或相同。至少在 bash 中,变量不能包含 NUL 字节,因此您不必担心正则表达式无法匹配这些字节。[0-9]globasciiranges[a-z][A-Z]

还请记住:

  • 在 bash 中使用readwithout-r和 withoutIFS=几乎没有任何意义。
  • echo不应该用于输出任意数据。
  • 错误应该发送到 stderr

相关内容