![测试字符串是否是数字](https://linux22.com/image/174629/%E6%B5%8B%E8%AF%95%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%98%AF%E5%90%A6%E6%98%AF%E6%95%B0%E5%AD%97.png)
这应该很简单,只需使用[[ "$var" =~ '^[1-9][0-9]*$' ]]
.但除了 zsh 之外,我没有得到我期望的行为。我不控制运行脚本的计算机,因此合理 shell 的可移植性(Solaris 旧版 Bourne shell 不合理)是一个问题。以下是一些测试:
% zsh --version
zsh 4.3.10 (x86_64-redhat-linux-gnu)
% zsh -c "[[ 100 =~ '^[1-9][0-9]*\$' ]] && echo OK"
OK
% sh --version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
% sh -c "[[ 100 =~ '^[1-9][0-9]*\$' ]] && echo OK"
% bash --version
GNU bash, version 4.2.53(1)-release (x86_64-unknown-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
% bash -c "[[ 100 =~ '^[1-9][0-9]*\$' ]] && echo OK"
% ksh --version
version sh (AT&T Research) 93u+ 2012-08-01
% ksh -c "[[ 100 =~ '^[1-9][0-9]*\$' ]] && echo OK"
%
我似乎错过了一些东西。什么?
答案1
测试字符串是否是数字
为此,您不需要正则表达式。使用case
语句将字符串与通配符模式进行匹配:它们不如正则表达式强大,但在这里已经足够了。看为什么我的正则表达式在 X 中有效但在 Y 中无效?如果您需要通配符模式(glob)与正则表达式语法有何不同的摘要。这适用于任何 sh 实现(甚至是 POSIX Bourne 之前的版本)。
case $var in
'' | *[!0123456789]*) echo >&2 "This is not a non-negative integer."; exit 2;;
[!0]*) echo >&2 "This is a positive integer. I like it.";;
0*[!0]*) echo >&2 "This is a positive integer written with a leading zero. I don't like it."; exit 2;;
*) echo >&2 "This number is zero. I don't like it."; exit 2;;
esac
外壳便携性
任何 Unix 系统都有 sh 的实现。任何非古董 Unix 或 POSIX 系统都有一个 sh 实现(大部分)遵循POSIX规范。它通常位于 中/bin/sh
,但也有一些商业 unice,其中/bin/sh
有一个古老的 Bourne shell 和现代 POSIX sh/usr/posix/bin/sh
等。
如果不适合您,可用作#!/usr/bin/env sh
实用的便携线。#!/bin/sh
[[ … ]]
在 POSIX sh 中不可用。它在 ksh93、mksh、bash 和 zsh 中可用,但在 dash(Linux 上流行)或 BusyBox(嵌入式 Linux 上/bin/sh
流行)中不可用。 /bin/sh
Portable sh 没有内置正则表达式匹配,只有通配符匹配。您可以使用 grep、awk 或 sed 在 POSIX 系统上获取正则表达式匹配。
引用正则表达式=~
=~
Ksh93、bash 和 zsh在条件表达式中具有正则表达式匹配运算符[[ … ]]
。它们的引用规则略有不同。
=~
在 bash ≥3.1 中,正则表达式字符只有在未加引号时才在运算符右侧具有特殊效果。 so[[ 100 =~ ^[1-9][0-9]*$ ]]
为 true 但[[ 100 =~ '^[1-9][0-9]*$' ]]
为 false([[ $x =~ '^[1-9][0-9]*$' ]]
仅匹配具有^[1-9][0-9]*$
子字符串的字符串)。
在 ksh 93u 中,在正则表达式中引用字符的效果取决于字符:不得引用同时也是通配符的字符,但不是的字符可以使用单引号或双引号(但前面不能有反斜杠) 。 so[[ 100 =~ ^[1-9][0-9]*$ ]]
为 true,so 为[[ 100 =~ '^'[1-9][0-9]*'$' ]]
but[[ 100 =~ '^[1-9][0-9]*$' ]]
为 false(它与子字符串 匹配任何内容[1-9][0-9]*
)并且[[ 100 =~ ^[1-9][0-9]*\$ ]]
也是 false(它与以非零数字开头的任何字符串匹配,然后是更多数字和 a $
)。
在 zsh 中,任何正则表达式字符都可以加引号或不加引号。请注意,这意味着要按字面意思包含字符,您需要两级引用,例如\\*
or'\*'
来匹配星号。所以 和[[ 100 =~ ^[1-9][0-9]*$ ]]
都是[[ 100 =~ '^[1-9][0-9]*$' ]]
正确的。
我认为将正则表达式放入变量中是不依赖于 shell 特性的最可靠方法。
regex='…' # Use extended regular expression syntax here, with '\'' if you need a literal apostrophe
if [[ $string =~ $regex ]]; …
正则表达式/通配符括号表达式中的范围
匹配的范围[0-9]
取决于实现和区域设置。一般来说,你不能指望它只匹配 0123456789 (尽管你应该能够假设它会匹配至少那些)。如果仅匹配 0123456789 很重要,请避免使用范围并单独命名字符。