脚本 Shell-如果满足两个条件,如何打印出回声?(仅限数字,exakt 2 个条件)

脚本 Shell-如果满足两个条件,如何打印出回声?(仅限数字,exakt 2 个条件)

我是 shell 脚本的新手,正在尝试学习基础知识。我正在使用终端启动一个脚本,例如“./scriptgoeshere.sh Argument1 Argument2”。

我希望脚本打印一些回声,“Hello World”可能是最好的选择,但我只希望它在以下情况下打印出 hello world:恰好两个参数,并且这些参数必须仅由数字组成。例如:

./scriptgoeshere.sh 1523 9643 ./scriptgoeshere.sh 7 96 应该可以工作,但是; ./scriptgoeshere.sh 594 arg 不应该工作,这是我现在拥有的脚本,但是它不起作用,我尝试了一整天来解决这个问题,但似乎无法解决它,我甚至尝试了 if/elif/else,在其中我检查 if 部分中是否有 2 个输入,以及它们是否都是 elif 中的数字,然后再继续。

#!/bin/bash
if [ "$#" -eq 2 ] && [ "$@" -eq ^[0-9] ]; 
then
    i=0
    while [ $i -lt 3 ]
    do
        echo "Hello World!"
        sleep 5
        i=$((i+1))
    done
else
echo "Need two arguments and only numbers will be accepted, such as ./scriptgoeshere 153 251"

fi

它会阻止 hello world 循环,直到我输入 2 个参数,但不管它们是数字还是文本,然后我都会收到错误:“./scriptgoeshere.sh:第 2 行:[: ^[0-9]:需要整数表达式”。即使我在终端中输入 ./scriptgoeshere.sh 123 123 来启动它,我也会收到该错误。我宁愿不通过变量来解决这个问题,因为我想在启动实际脚本时找出参数。

这让我很头疼!

我开始用这个来检查是否正好有 2 个参数,并且它能正常工作,我只是想对它进行一个小修复,这样这两个参数就必须是数字了。我不明白为什么这看起来这么难。

#!/bin/bash
if [ "$#" -eq 2 ];
then
    i=0
    while [ $i -lt 3 ]
    do
        echo "Hello World!"
        sleep 1
        i=$((i+1))
    done
else
echo "Need two argument, only numbers accepted"

fi

编辑:我解决了。我已经很接近了。。我只是需要在 if 字符串的数字部分添加额外的 []。有人知道为什么它需要写成 [[ "$@" -eq ^[0-9] ]] 而第一部分只需一个 [] 就可以写成吗?

答案1

一开始[ "$#" -eq ^[0-9] ]或者[ "$@" -eq ^[0-9] ](我在写答案的时候编辑了这个问题……)。从上下文来看,我认为你想测试参数是否是数字。

  • $#始终是数字,即十进制位置参数的数量。您要测试两个位置参数:$1$2
  • [是一个命令([ whatever ]等同于test whatever)。它可能是shell内建命令,但在语法上它类似于lsman。后面的单词(包括])由shell像解析命令后面的任何单词一样进行解析:shell将它们扩展(例如,$#变为数字)、删除引号;最终它们成为的参数[。这些扩展之一是文件名扩展,其中各种模式与现有文件名进行匹配(文件名扩展、通配符)。如果这些文件存在,未加引号的^[0-9]可能会扩展为^2 ^5。如果没有匹配项或者您禁用该功能,^[0-9]将会按字面意思获取到[(作为其参数之一)。它看起来像一个模式,但[不支持模式匹配。
  • -eq要求前一个和下一个参数为整数。$#肯定会扩展为整数。在此上下文中,无法^[0-9]扩展为数字。
  • 双引号$@很特殊,尽管被引用,它仍然可以扩展为多个单词(POSIX 术语中的“字段”)。通常我们引用以防止单词拆分:"$foo"即使值包含空格等,也会扩展为一个单词。"$@"像 一样扩展"$1" "$2" …,它的双引号可以防止$1扩展为多个单词(和单独$2等)。在此片段中:

    [ "$#" -eq 2 ] && [ "$@" -eq ^[0-9] ]
    

    [当且仅当第一个测试成功时,才会运行第二个测试(这是&&工作原理),即当且仅当有两个位置参数。在这种情况下,"$@"相当于"$1" "$2"第二个测试变为

    [ "$1" "$2" -eq ^[0-9] ]
    

    这没有意义。[可能会根据参数的实际值和的扩展以不同的方式抱怨^[0-9],但它仍然是垃圾。


现在关于[[ "$#" = [0-9] ]]

注意:此片段出现在问题的第一次修订中。我在撰写答案时编辑了这个问题。我决定保留此片段,因为[[无论如何最终可能会有用。

  • 再说一遍:$#始终是一个数字。
  • [[不等同于[。任何你可以用 进行的合理测试[都可以用 重写[[(尽管你不应该盲目地用 替换[[[。它还可以执行其他测试[[,功能更强大。虽然POSIX 要求[(和),但 不是。test[[

    重要的是:[[不是像ls或 这样的命令[。它是一个关键字,它改变了 shell 解析后面单词的方式。[[和之间的所有内容]]的解析方式都不同。

    如果操作符是=,则下一个参数是模式。某些字符/语法(例如*[0-9])在未加引号时与 之前的参数匹配=。这就像文件名扩展,但不针对现有文件名。您的代码测试了(扩展)是否$#正好是一位数字。整个测试是

    [[ "$#" -eq 2 ]] && [[ "$#" = [0-9] ]]
    

    foo && barbar当且仅当成功时才运行foo。第二个仅在扩展为时才[[进行测试,因此它必须成功,因为恰好是一位数字。在这种情况下,第二个没有任何变化。$#22[[


假设您意识到了自己的错误并测试它是否$1是一个数字。

  • 与上述类似,如果扩展为一位数字则[[ "$1" = [0-9] ]]返回成功。$1
  • [[ "$1" = [0-9][0-9] ]]– 正好两位数字。
  • [[ "$1" = [0-9]* ]]– 以数字开头的内容。

要检测任意数量的数字,您需要=~。 里面的这个运算符[[ ]]有点类似于=,但现在模式是一个扩展的正则表达式。 不过,它不需要匹配运算符左侧的整个字符串,一个子字符串就足够了(因此,在您的例子中需要^和锚点):$

  • [[ "$1" =~ [0-9] ]]$1如果扩展为包含数字的字符串则返回成功。
  • [[ "$1" =~ [0-9][0-9] ]]– …包含两个相邻的数字。
  • [[ "$1" =~ ^[0-9]$ ]]– 恰好由一位数字组成的字符串。
  • [[ "$1" =~ ^[0-9]*$ ]]– 零个或多个数字。
  • [[ "$1" =~ ^[0-9][0-9]*$ ]]– 一个或多个数字。
  • [[ "$1" =~ ^[0-9]+$ ]]– 一个或多个数字。

[[ "$1" =~ ^[0-9]+$ ]]以及[[ "$2" =~ ^[0-9]+$ ]]您可能需要的测试。为了允许-像这样修改它们:[[ "$1" =~ ^-?[0-9]+$ ]]。整个测试片段可能是

[ "$#" -eq 2 ] && `[[ "$1" =~ ^-?[0-9]+$ ]]` && `[[ "$2" =~ ^-?[0-9]+$ ]]`

变量/参数内部[[ ]]不能使用双引号,并且其扩展值不会进行分词和通配。因此您可以[[ $1 =~ ^-?[0-9]+$ ]]放心地编写。这是例外;通常您应该使用双引号来引用变量。在这里使用双引号不会造成任何干扰,因此我建议无论如何都要使用双引号来习惯这种良好的通用做法。


您声称您使用 解决了问题[[ "$@" -eq ^[0-9] ]]。抱歉,我不相信您。如果恰好有两个位置参数并且它们是数字,我看不出这在语法上是正确的,更不用说测试您想要的了。在其他情况下……我仍然看不出有什么办法。

… 使用[[ "$@" =~ ^[0-9] ]](从您的评论来看,与问题不一致)。这不是一个合适的解决方案。我的测试表明"$@"inside 的[[ ]]行为就像未引用的$@单词一样,没有进行单词拆分和通配。这与常规变量 inside 的行为非常相似[[ ]],尽管我事先并不明显,因为我知道"$@"它不像常规变量。

附注:出于这个原因,我从未想过使用"$@"inside [[ ]]。我无法想象这是正确的选择。

当恰好有两个位置参数时,您的“解决方案”将变成[[ "$1 $2" =~ ^[0-9] ]]。它在语法上有效,并且会检查脚本的第一个参数的第一个字符是否是数字。仅此而已。运行./scriptgoeshere.sh 0h well,参数将通过测试。


笔记:

  • [0-9](作为通配符模式或正则表达式)取决于您当前的语言环境。它很可能会按预期工作,但从技术上讲,可以有一个语言环境,其中9位于 之前02位于 之后0-9。匹配数字的最通用方法是[[:digit:]]。请参阅

    另一方面,您可能希望将$1and/or$2与 shell 算术 ( $((…))) 一起使用,[ "$1" -ge 50 ]或类似方法。那么您只需要阿拉伯数字。即使[[:digit:]]在您的语言环境中包含它们(可能确实如此),它也可能包含其他数字。考虑[0123456789];它是明确的并且不依赖于语言环境。

  • 从and/or^-?[0-9]+$的角度来看,并非每个匹配的字符串都被视为整数(例如,当您执行 时)。每个工具都有其可以处理的整数范围。同样,如果您向 shell 算术提供描述超出范围的整数的数字字符串,则它可能会生成数学上错误的结果。[[[[ "$1" -eq 0 ]

  • 从数学上来说1.00是一个整数。有些工具可能接受它为整数,有些则不接受。那呢1e100

相关内容