这个shell脚本函数的含义是什么

这个shell脚本函数的含义是什么

有人可以举个例子告诉我每一行的含义是什么吗,我不明白为什么使用正则表达式,甚至不明白[!0122...]

#!/bin/sh
is_integer ()
{
    case "${1#[+-]}" in
        (*[!0123456789]*) return 1 ;;
        ('')              return 1 ;;
        (*)               return 0 ;;
    esac
}

答案1

#!/bin/sh

shell 的语法中有一个注释。但是,这#!告诉内核,在执行该文件时,/bin/sh应该使用存储在该路径中的解释器来解释该文件,并且应该使用脚本的路径作为参数来执行。

is_integer () compound-command

是定义函数的POSIX sh语法。

{
   ...
}

是一个复合命令称为指挥组。它的唯一目的是对命令进行分组,在这里使其成为函数的主体。在这里,它是多余的,因为它的内容只有一个复合命令,但是使用{ ... }命令组作为每个函数的主体是常见的做法,并且可以使代码更具可读性,因此通常建议这样做。可以编写相同的函数:

is_integer () case "${1#[+-]}" in
  (*[!0123456789]*) return 1 ;;
  ('')              return 1 ;;
  (*)               return 0 ;;
esac

case something in (pattern1 | pattern2) ...;; (pattern3)... ; esac是一个case/esac结构(组成一个复合命令something依次匹配每个模式,并在第一次匹配时执行相应的代码。

something${1#[-+]}。那是一个参数扩展,它将${param#pattern}运算符应用于1作为函数第一个参数的参数。该运算符从参数内容的开头删除与模式匹配的最短字符串。是匹配或字符[-+]的通配符模式(不是正则表达式)。因此扩展到去掉符号的第一个参数的值。因此,如果第一个参数是,则变为 2。如果是,则变为空字符串。如果是的话那就留下来吧。-+${1#[-+]}-2-22

你会注意到"${1#[+-]}"被引用了。通常,您需要引用参数扩展,否则它们将受到 split+glob 的影响。在这里,这是极少数不会发生这种情况的情况之一,因此严格来说,这些引用是多余的(但不会造成伤害,并且仍然是很好的做法)。

然后将该值与某些模式进行匹配。

*[!0123456789]**-- 任意数量的字符(尽管大多数 shell 也接受非字符) -- 后跟 -- 任何既不[!0123456789]0也不是1... 也不是的字符9-- 后跟任意数量的字符(*再次)。因此,它将匹配任何包含非十进制数字的字符(或大多数 shell 中的非字符)的字符串。

如果匹配,则return 1执行代码,这将导致函数返回1退出代码,就像 0 以外的任何数字一样错误的/失败

''是表示空字符串的一种方法。空字符串也不是有效的数字,但不会与先前的模式匹配。

然后*匹配任何东西。因此,return 0将针对与任何先前模式都不匹配的任何字符串运行。它在这里是多余的,因为该case语句是该函数中的最后一个命令,并且case语句返回成功/真的如果里面没有运行命令。

所以在这里,该函数定义可以缩短为:

is_integer() case ${1#[-+]} in
  ('' | *[!0123456789]*) false
esac

尽管这并没有使它更清晰。

无论如何,使用该代码是正确的[0123456789]。特别是对于输入验证(当在 shell 算术表达式中使用输入时验证输入至关重要,请参阅在 Shell 算术评估中使用未经净化的数据的安全影响),[0-9]或者[[:digit:]]应该不是可以使用,特别是如果您的sh实现bash可以[0-9]匹配在 0 到 9 之间排序的任何字符(或者可能是多字符排序元素),并且[[:digit:]]在某些 BSD 上将匹配任何十进制数字系统的数字,而不仅仅是 0123456789 英文系统的数字,即使在英语语言环境中也是如此。

例如,在 GNU 系统上,在典型的美国英语语言环境中(现在倾向于使用 UTF-8 作为其字符集), in bash,[0-9]也会匹配

答案2

它返回真的(零)如果函数的第一个参数是整数,并且错误的(1) 如果不是。

它通过首先删除第一个参数值开头的任何单个+或符号来实现此目的。-这就是作用"${1#[+-]}"。这是使用标准参数扩展,它从变量值的开头${variable#pattern}删除最短的子字符串匹配。该模式应该是 shell 通配模式,而不是正则表达式。patternvariable

然后,它通过一系列模式匹配(通配模式,而不是正则表达式)运行结果值。第一个匹配的模式将触发相应的return语句。

第一个模式测试字符串中是否存在数字以外的其他字符。这个模式也可以被写成*[!0-9]**[![:digit:]]*(但另见这里)。该函数与 a在正则表达式字符类或范围中执行的!方式相同(即如 中所示,并且某些 shell 也在这里接受 a ),即它反转给定的字符类或范围。该模式可以理解为“匹配给定字符串中任意位置的非数字”。模式开头和结尾处的是必需的,因为 shell 通配模式始终是锚定的(相应的正则表达式是, 或 ,具有显式不需要的锚定)。^[^...]^*[!0123456789]**[^0-9]^.*[^0-9].*$

第二种模式只是测试字符串是否为空。

最后一个模式匹配任何字符串。

in 函数的替代实现bash(允许与==inside进行模式匹配[[ ... ]]):

is_integer () {
    set -- "${1#[+-]}"

    if [ -z "$1" ] || [[ $1 == *[!0-9]* ]]; then
        return 1
    fi

    return 0
}

相关内容