测试一个变量在 bash 脚本中是否有超过 4 位的数字

Elm*_*ao_ 2 bash shell-script

我想测试一个变量是否有超过 4 位这样的数字

#!/bin/bash
if [ $input has more than 4 digits ]; then 
     echo "  * Please only 4 digits" >&2
     echo""
else
   the other option
fi
Run Code Online (Sandbox Code Playgroud)

Sté*_*las 7

这里假设您的意思是 ASCII 十进制数字,而不是其他类型的十进制或非十进制数字。

shopt -s extglob # enables a subset of ksh extended globs including *(...),
                 # +(...) and ?(...) but unfortunately not {4}(...)

d='[0123456789]' nd='[^0123456789]'

case $input in
  ( $d$d$d$d+($d)     ) echo made of more than 4 digits;;
  ( *$d*$d*$d*$d*$d*  ) echo contains more than 4 digits;;
  ( ""                ) echo empty;;
  ( *($nd)            ) echo does not contain any digit;;
  ( *$nd*             ) echo no more than 4 digits but also contains non-digits;;
  ( $d?($d)?($d)?($d) ) echo made of 1 to 4 digits;;
  ( *                 ) echo should not be reached;;
esac
Run Code Online (Sandbox Code Playgroud)

请注意,bash取决于系统和语言环境,[0-9]并且[[:digit:]]可能匹配的不仅仅是 0123456789,因此不应将它们用于输入验证(例如,在对不同问题的回答中有更多内容)。

还要注意bash模式匹配在多字节语言环境中以非常令人惊讶的方式工作

您会发现,例如在zh_CN.gb18030中文语言环境中,input='1-©©'它会no more than 4 digits but also contains non-digits按预期返回,但是如果附加一个0x80字节 ( input='1-©©'$'\x80'),它将返回contains more than 4 digits.

正是出于这种原因(以及已知模式匹配在许多 shell 中的极端情况下存在错误这一事实),对于输入验证,最好对您接受的事物尽可能使用正匹配(而不是负匹配)对于要拒绝的事物)¹ 因此$d?($d)?($d)?($d),即使至少在理论上它不是必需的,但其他任何东西都应该与早期的模式相匹配。


¹ 作为例外,人们可能需要考虑 Bourne 和 Korn shell 的错误特征,即case $input in [x]) echo yes; esac匹配x但也匹配[x]!


gle*_*man 5

我会做

#!/usr/bin/env bash

die () { echo "$*" >&2; exit 1; }

input=$1
[[ $input == +([[:digit:]]) ]] || die "only digits please"
(( input <= 9999 ))            || die "no more than 4 digits please"
echo "ok: $input"
Run Code Online (Sandbox Code Playgroud)


ilk*_*chu 5

如果您关心位数(而不是数值),您可以匹配 Bash/Ksh/Zsh 中的正则表达式(* 见脚注[[:digit:]]

#!/bin/bash
input=$1
re='^[[:digit:]]{1,4}$'
if [[ $input =~ $re ]]; then
    echo "'$input' contains 1 to 4 digits (and nothing else)"
else
    echo "'$input' contains something else"
fi
Run Code Online (Sandbox Code Playgroud)

或者例如[[ $input =~ ^[[:digit:]]{5,}$ ]]检查“5 位或更多位数字(没有别的)”等。


或者在纯 POSIX shell 中,您必须在其中使用case模式匹配:

#!/bin/sh
input=$1
case $input in 
    *[![:digit:]]*) onlydigits=0;; # contains non-digits
    *[[:digit:]]*)  onlydigits=1;; # at least one digit
    *)              onlydigits=0;; # empty
esac

if [ $onlydigits = 0 ]; then
    echo "'$input' is empty or contains something other than digits"
elif [ "${#input}" -le 4 ]; then
    echo "'$input' contains 1 to 4 digits (and nothing else)"
else
    echo "'$input' contains 5 or more digits (but nothing else)"
fi
Run Code Online (Sandbox Code Playgroud)

(你可以把所有的逻辑放在case,但嵌套if那里有点难看,IMO。)


请注意,它[[:digit:]]应该与当前语言环境的“数字”概念相匹配。这可能会也可能不会超过 ASCII 数字0123456789。在我的系统上,[[:digit:]]不匹配,例如?(上标四,U+2074),但[0-9]确实如此。匹配其他“数字”可能是一个问题,尤其是。如果您对外壳中的数字进行算术运算。因此,如果您想更严格,请使用[0123456789]仅接受 ASCII 数字。