zoukankan      html  css  js  c++  java
  • Linux Shell 数学运算

    1、执行数学运算

      另一个对任何编程语言都很重要的特性是操作数字的能力。遗憾的是,对shell脚本来说,这个处理过程会比较麻烦。在shell脚本中有两种途径来进行数学运算。

    1.1 expr 命令

      最开始,Bourne shell提供了一个特别的命令用来处理数学表达式。expr命令允许在命令行上处理数学表达式,但是特别笨拙。

    1 $ expr 1 + 5
    2 6

      注意:1 + 5,+ 号左右两边均有空格,如两边均无空格,shell会认为这是一个字符串!!!

      expr命令能够识别少数的数学和字符串操作符,见下表。

    操 作 符 描 述
    ARG1 | ARG2 如果ARG1既不是null也不是零值,返回ARG1;否则返回ARG2
    ARG1 & ARG2 如果没有参数是null或零值,返回ARG1;否则返回0
    ARG1 < ARG2 如果ARG1小于ARG2,返回1;否则返回0
    ARG1 <= ARG2 如果ARG1小于或等于ARG2,返回1;否则返回0
    ARG1 = ARG2 如果ARG1等于ARG2,返回1;否则返回0
    ARG1 != ARG2 如果ARG1不等于ARG2,返回1;否则返回0
    ARG1 >= ARG2 如果ARG1大于或等于ARG2,返回1;否则返回0
    ARG1 > ARG2 如果ARG1大于ARG2,返回1;否则返回0
    ARG1 + ARG2 返回ARG1和ARG2的算术运算和
    ARG1 - ARG2 返回ARG1和ARG2的算术运算差
    ARG1 * ARG2 返回ARG1和ARG2的算术乘积
    ARG1 / ARG2 返回ARG1被ARG2除的算术商
    ARG1 % ARG2 返回ARG1被ARG2除的算术余数
    STRING : REGEXP 如果REGEXP匹配到了STRING中的某个模式,返回该模式匹配
    match STRING REGEXP 如果REGEXP匹配到了STRING中的某个模式,返回该模式匹配
    substr STRING POS LENGTH 返回起始位置为POS(从1开始计数)、长度为LENGTH个字符的子字符串
    index STRING CHARS 返回在STRING中找到CHARS字符串的位置;否则,返回0
    length STRING 返回字符串STRING的数值长度
    + TOKEN 将TOKEN解释成字符串,即使是个关键字
    (EXPRESSION) 返回EXPRESSION的值

      尽管标准操作符在expr命令中工作得很好,但在脚本或命令行上使用它们时仍有问题出现。许多expr命令操作符在shell中另有含义(比如星号)。当它们出现在在expr命令中时,会得到一些诡异的结果。

    1 $ expr 5 * 2
    2 expr: syntax error
    3 $

      要解决这个问题,对于那些容易被shell错误解释的字符,在它们传入expr命令之前,需要使用shell的转义字符(反斜线)将其标出来。

    1 $ expr 5 * 2
    2 10
    3 $

      现在,麻烦才刚刚开始!在shell脚本中使用expr命令也同样复杂:

    1 $ cat expr.sh 
    2 #!/bin/bash
    3 #An example of using expr command
    4 var1=20
    5 var2=10
    6 var3=$(expr ${var1} / ${var2})
    7 echo the result is ${var3}
    8 $

      要将一个数学算式的结果赋给一个变量,需要使用命令替换来获取expr命令的输出:

    1 $ chmod a+x expr.sh
    2 $ /expr.sh
    3 the result is 2
    4 $

      幸好bash shell有一个针对处理数学运算符的改进,将会在后面介绍中看到。

    1.2 使用方括号

      bash shell为了保持跟Bourne shell的兼容而包含了expr命令,但它同样也提供了一种更简单的方法来执行数学表达式。在bash中,在将一个数学运算结果赋给某个变量时,可以用美元符和方括号($[ operation ])将数学表达式围起来。

    1 $ var1=$[1 + 5]
    2 $ echo $var1
    3 6
    4 $ var2=$[10 / 2]
    5 $ echo $var2
    6 5
    7 $ 

      用方括号执行shell数学运算比用expr命令方便很多。这种技术也适用于shell脚本。

     1 $ chmod a+x expr1.sh 
     2 $ cat expr1.sh 
     3 #!/bin/bash
     4 
     5 var1=50
     6 var2=40
     7 var3=35
     8 
     9 var4=$[$var1 * ($var2 - $var3)]
    10 echo the result is $var4
    11 $ ./expr1.sh 
    12 the result is 250
    13 $ 

      同样,注意在使用方括号来计算公式时,不用担心shell会误解乘号或其他符号。shell知道它不是通配符,因为它在方括号内。

      在bash shell脚本中进行算术运算会有一个主要的限制。请看下例:

     1 $ cat expr2.sh 
     2 #!/bin/bash
     3 
     4 var1=20
     5 var2=3
     6 
     7 var3=$[$var1 / $var2]
     8 echo the final result is $var3
     9 $ chmod a+x expr2.sh 
    10 $ ./expr2.sh 
    11 the final result is 6
    12 $

      bash shell数学运算符只支持整数运算。若要进行任何实际的数学计算,这是一个巨大的限制。

      说明 z shell(zsh)提供了完整的浮点数算术操作。如果需要在shell脚本中进行浮点数运算,可以考虑看看z shell(将在后面博客中讨论)。

    2、浮点解决方案

      有几种解决方案能够克服bash中数学运算的整数限制。最常见的方案是用内建的bash计算器,叫作bc。

    2.1 bc的基本用法

      bash计算器实际上是一种编程语言,它允许在命令行中输入浮点表达式,然后解释并计算该表达式,最后返回结果。bash计算器能够识别:

    •   数字(整数和浮点数)
    •   变量(简单变量和数组)
    •   注释(以#或C语言中的/* */开始的行)
    •   表达式
    •   编程语句(例如if-then语句)
    •   函数
    •   可以在shell提示符下通过bc命令访问bash计算器:
     1 $ bc
     2 bc 1.07.1
     3 Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
     4 This is free software with ABSOLUTELY NO WARRANTY.
     5 For details type `warranty'. 
     6 3.3 * 5
     7 16.5
     8 3.14 * (3 + 4)
     9 21.98
    10 quit
    11 $ 

      这个例子一开始输入了表达式3.3 * 5。bash计算器返回了计算结果。随后每个输入到计算器的表达式都会被求值并显示出结果。要退出bash计算器,你必须输入quit。
      浮点运算是由内建变量scale控制的。必须将这个值设置为你希望在计算结果中保留的小数位数,否则无法得到期望的结果。

    1 $ bc -q
    2 4 / 5
    3 0
    4 scale=3
    5 4 / 5
    6 .800
    7 quit
    8 $

      -q, --quiet  Do not print the normal GNU bc welcome.

      scale变量的默认值是0。在scale值被设置前,bash计算器的计算结果不包含小数位。在将其值设置成4后,bash计算器显示的结果包含四位小数

      除了普通数字,bash计算器还能支持变量。

    1 $ bc -q
    2 var1=10
    3 var1 * 4
    4 40
    5 var2 = var1 / 5
    6 print var2
    7 2
    8 quit
    9 $

      变量一旦被定义,你就可以在整个bash计算器会话中使用该变量了。print语句允许你打印变量和数字。

    2.2 在脚本中使用bc

      现在你可能想问bash计算器是如何在shell脚本中帮助处理浮点运算的。还记得命令替换吗?是的,可以用命令替换运行bc命令,并将输出赋给一个变量。基本格式如下:

      variable=$(echo "options; expression" | bc)

      第一部分options允许你设置变量。如果你需要不止一个变量,可以用分号将其分开。expression参数定义了通过bc执行的数学表达式。

    1 $ cat bc1.sh 
    2 #!/bin/bash
    3 var1=$(echo "scale=4; 10 / 4" | bc)
    4 echo the answer is $var1
    5 $

      这个例子将scale变量设置成了四位小数,并在expression部分指定了特定的运算。运行这个脚本会产生如下输出。

    1 $ ./bc1.sh 
    2 the answer is 2.5000
    3 $

      太好了!现在你不会再只能用数字作为表达式值了。也可以用shell脚本中定义好的变量。

    1 $ cat bc2.sh 
    2 #!/bin/bash
    3 
    4 var1=100
    5 var2=45
    6 
    7 var3=$(echo "scale=4; ${var1} / ${var2}" | bc)
    8 echo the answer is ${var3}
    9 $ 

      脚本定义了两个变量,它们都可以用在expression部分,然后发送给bc命令。别忘了用美元符表示的是变量的值而不是变量自身。这个脚本的输出如下。

    1 $ chmod a+x bc2.sh 
    2 $ ./bc2.sh 
    3 the answer is 2.2222
    4 $ 

      当然,一旦变量被赋值,那个变量也可以用于其他运算。

     1 $ cat bc3.sh 
     2 #!/bin/bash
     3 
     4 var1=10
     5 var2=3.1415926
     6 var3=$(echo "scale=4; ${var1} * ${var2}" | bc)
     7 var4=$(echo "scale=4; ${var3} * ${var2}" | bc)
     8 
     9 echo the result is ${var4}
    10 $ chmod a+x bc3.sh 
    11 $ ./bc3.sh 
    12 the result is 98.6960406
    13 $ 

      这个方法适用于较短的运算,但有时你会涉及更多的数字。如果需要进行大量运算,在一个命令行中列出多个表达式就会有点麻烦。
      有一个方法可以解决这个问题。bc命令能识别输入重定向,允许你将一个文件重定向到bc命令来处理。但这同样会叫人头疼,因为你还得将表达式存放到文件中。
      最好的办法是使用内联输入重定向,它允许你直接在命令行中重定向数据。在shell脚本中,你可以将输出赋给一个变量。

    1 variable=$(bc << EOF
    2 options
    3 statements
    4 expressions
    5 EOF
    6 )

      EOF文本字符串标识了内联重定向数据的起止。记住,仍然需要命令替换符号将bc命令的输出赋给变量。

      现在可以将所有bash计算器涉及的部分都放到同一个脚本文件的不同行。下面是在脚本中使用这种技术的例子。

     1 $ cat bc4.sh 
     2 #!/bin/bash
     3 
     4 var1=11.11
     5 var2=22.22
     6 var3=33.33
     7 var4=44.44
     8 
     9 var5=$(bc << EOF
    10 scale=5
    11 a1=(${var1} * ${var2})
    12 b1=(${var3} * ${var4})
    13 a1+b1
    14 EOF
    15 )
    16 
    17 echo the answer is ${var5}
    18 $ chmod a+x bc4.sh 
    19 $ ./bc4.sh 
    20 the answer is 1728.0494
    21 $ 

      将选项和表达式放在脚本的不同行中可以让处理过程变得更清晰,提高易读性。EOF字符串标识了重定向给bc命令的数据的起止。当然,必须用命令替换符号标识出用来给变量赋值的命令。
      你还会注意到,在这个例子中,你可以在bash计算器中赋值给变量。这一点很重要:在bash计算器中创建的变量只在bash计算器中有效,不能在shell脚本中使用。

    3、退出脚本

      迄今为止所有的示例脚本中,我们都是突然停下来的。运行完最后一条命令时,脚本就结束了。其实还有另外一种更优雅的方法可以为脚本划上一个句号。
      shell中运行的每个命令都使用退出状态码(exit status)告诉shell它已经运行完毕。退出状态码是一个0~255的整数值,在命令结束运行时由命令传给shell。可以捕获这个值并在脚本中使用。

    3.1、查看退出状态码

      Linux提供了一个专门的变量$?来保存上个已执行命令的退出状态码。对于需要进行检查的命令,必须在其运行完毕后立刻查看或使用$?变量。它的值会变成由shell所执行的最后一条命令的退出状态码。

    1 $ date
    2 Sat Jan 15 10:01:30 EDT 2020
    3 $ echo $?
    4 0
    5 $

      按照惯例,一个成功结束的命令的退出状态码是0。如果一个命令结束时有错误,退出状态码就是一个正数值。

    1 $ asdfg
    2 -bash: asdfg: command not found
    3 $ echo $?
    4 127
    5 $
    1 $ asdfg
    2 -bash: asdfg: command not found
    3 $ echo $?
    4 127
    5 $

      无效命令会返回一个退出状态码127。Linux错误退出状态码没有什么标准可循,但有一些可用的参考,如下表所示。

                 

       退出状态码126表明用户没有执行命令的正确权限。

    1 $ ./myprog.c
    2 -bash: ./myprog.c: Permission denied
    3 $ echo $?
    4 126
    5 $

      另一个会碰到的常见错误是给某个命令提供了无效参数。

    1 $ date %t
    2 date: invalid date '%t'
    3 $ echo $?
    4 1
    5 $

      这会产生一般性的退出状态码1,表明在命令中发生了未知错误。

    3.2、exit 命令

      默认情况下,shell脚本会以脚本中的最后一个命令的退出状态码退出。

    1 $ ./test6
    2 The result is 2
    3 $ echo $?
    4 0
    5 $

      你可以改变这种默认行为,返回自己的退出状态码。exit命令允许你在脚本结束时指定一个退出状态码。

    1 $ cat test13
    2 #!/bin/bash
    3 # testing the exit status
    4 var1=10
    5 var2=30
    6 var3=$[$var1 + $var2]
    7 echo The answer is $var3
    8 exit 5
    9 $

      当查看脚本的退出码时,你会得到作为参数传给exit命令的值。

    1 $ chmod u+x test13
    2 $ ./test13
    3 The answer is 40
    4 $ echo $?
    5 5
    6 $

      也可以在exit命令的参数中使用变量。

    1 $ cat test14
    2 #!/bin/bash
    3 # testing the exit status
    4 var1=10
    5 var2=30
    6 var3=$[$var1 + $var2]
    7 exit $var3
    8 $

      当你运行这个命令时,它会产生如下退出状态。

    1 $ chmod u+x test14
    2 $ ./test14
    3 $ echo $?
    4 40
    5 $

      你要注意这个功能,因为退出状态码最大只能是255。看下面例子中会怎样。

    1 $ cat test14b
    2 #!/bin/bash
    3 # testing the exit status
    4 var1=10
    5 var2=30
    6 var3=$[$var1 * $var2]
    7 echo The value is $var3
    8 exit $var3
    9 $

      现在运行它的话,会得到如下输出。

    1 $ ./test14b
    2 The value is 300
    3 $ echo $?
    4 44
    5 $

      退出状态码被缩减到了0~255的区间。shell通过模运算得到这个结果。一个值的模就是被除=后的余数。最终的结果是指定的数值除以256后得到的余数。在这个例子中,指定的值是300(返=回值),余数是44,因此这个余数就成了最后的状态退出码。
      在后面中,你会了解到如何用if-then语句来检查某个命令返回的错误状态,以便知道命=令是否成功。

  • 相关阅读:
    VC下使用Proc连接Oracle数据库
    解决ORACLE账号system被锁和修改密码
    Javascript 操作select控件大全(新增、修改、删除、选中、清空、判断存在等)[转]
    ckeditor用fckeditor的文件管理器实现图片上传
    video 播放多个视频
    web worker 发送Ajax
    对投影纹理映射的一些思考
    一个光线跟踪的简单实例
    【转载】齐次坐标概念&&透视投影变换推导
    今天开通了cnblog
  • 原文地址:https://www.cnblogs.com/Reverse-xiaoyu/p/13128675.html
Copyright © 2011-2022 走看看