这个shell脚本函数是什么意思

dis*_*vox -2 shell shell-script

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

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

Sté*_*las 10

#!/bin/sh
Run Code Online (Sandbox Code Playgroud)

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

is_integer () compound-command
Run Code Online (Sandbox Code Playgroud)

是用于定义函数的 POSIX sh 语法。

{
   ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

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

如果匹配,return 1则执行代码,这将导致函数返回该1退出代码,与 0 以外的任何数字一样,表示false / failure

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

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

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

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

虽然这并没有使它更清晰。

在任何情况下,该代码都适合使用[0123456789]. 特别是对于输入验证(和它的当它在外壳算术表达式的二手验证输入的关键,看到壳牌算术评估使用unsanitized数据的安全影响),[0-9]或者[[:digit:]]应该被使用,特别是如果你sh的实现是bash因为[0-9]可以匹配任何字符(或可能的多字符排序元素)在 0 和 9 之间排序,并且[[:digit:]]在某些 BSD 上将匹配任何十进制数字系统的数字,不仅是 0123456789 英语系统,甚至在英语语言环境中也是如此。

例如,GNU系统上,在一个典型的美国英语语言环境(这些天倾向于使用UTF-8字符集的),在bash[0-9]还会匹配上和数以百计的其他字符)。而在 FreeBSD 上,在相同的语言环境中,[[:digit:]]将匹配数百个不同的字符(包括)。

例如,如果您在输入验证期间通过,则不会关闭那些任意代码注入漏洞的路径。在kshGNU 系统中和在 GNU 系统上,是一个有效的变量名(对于由 匹配的许多其他字符也是如此[0-9])。如果该变量已设置(例如在环境中)并包含a[0$(reboot>&2)],则:

is_integer "$1" || exit
echo "$(( $1 + 1 ))"
Run Code Online (Sandbox Code Playgroud)

如果is_integer无法拒绝该输入,则在 ksh 中将导致重新启动。

要使用正则表达式进行匹配,您需要expror awk,尽管很少有 shell 内置这些命令,因此效率会降低。一些[实现,比如[内置的zshoryash也可以进行正则表达式匹配。并且一些 shell 还具有[[ ... ]]可以进行正则表达式匹配的条件表达式构造,但这些都不是标准的,sh并且在输入验证方面有自己的问题。

虽然*大多数sh实现中的shell 通配符将匹配字节序列,即使其中一些不形成有效字符,同样 for [!0123456789].*[^0123456789]regexp 等价物通常不会。

在这里,只要匹配是肯定的,这可能不是问题。做一个负匹配,如:

regexp() {
  awk -- 'BEGIN {exit !(ARGV[1] ~ ARGV[2])}' "$@"
}

is_integer() {
  ! regexp "${1#[-+]}" '^(.*[^012345679].*)?$'
}
Run Code Online (Sandbox Code Playgroud)

由于该case语句的直接翻译是错误的,因为它无法拒绝包含未形成有效字符的字节序列的输入,但是

is_number() {
  regexp "$1" '^[-+]?[0123456789]+$'
}
Run Code Online (Sandbox Code Playgroud)

应该没问题,因为它会拒绝任何包含未形成有效字符的字节序列的输入。