测试字符串是否是数字

测试字符串是否是数字

这应该很简单,只需使用[[ "$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/shPortable 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 很重要,请避免使用范围并单独命名字符。

相关内容