当前位置:K88软件开发文章中心网站服务器框架Shell → 文章内容

Shell 数值运算

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-23 14:39:33

由 娃娃脸呦 创建,Loen 最后一次修改 2016-02-24 前言从本文开始,打算结合平时积累和进一步实践,通过一些范例来介绍Shell编程。因为范例往往能够给人以学有所用的感觉,而且给人以动手实践的机会,从而激发人的学习热情。考虑到易读性,这些范例将非常简单,但是实用,希望它们能够成为我们解决日常问题的参照物或者是“茶余饭后”的小点心,当然这些“点心”肯定还有值得探讨、优化的地方。更复杂有趣的例子请参考 Advanced Bash-Scripting Guide (一本深入学习 Shell 脚本艺术的书籍)。该系列概要:目的:享受用 Shell 解决问题的乐趣;和朋友们一起交流和探讨。计划:先零散地写些东西,之后再不断补充,最后整理成册。读者:熟悉 Linux 基本知识,如文件系统结构、常用命令行工具、Shell 编程基础等。建议:看范例时,可参考《Shell基础十二篇》和《Shell十三问》。环境:如没特别说明,该系列使用的 Shell 将特指 Bash,版本在 3.1.17 以上。说明:该系列不是依据 Shell 语法组织,而是面向某些潜在的操作对象和操作本身,它们反应了现实应用。当然,在这个过程中肯定会涉及到 Shell 的语法。这一篇打算讨论一下 Shell 编程中的基本数值运算,这类运算包括:数值(包括整数和浮点数)间的加、减、乘、除、求幂、求模等产生指定范围的随机数产生指定范围的数列Shell 本身可以做整数运算,复杂一些的运算要通过外部命令实现,比如 expr,bc,awk 等。另外,可通过 RANDOM 环境变量产生一个从 0 到 32767 的随机数,一些外部工具,比如 awk 可以通过 rand() 函数产生随机数。而 seq 命令可以用来产生一个数列。下面对它们分别进行介绍。整数运算范例:对某个数加 1$ i=0;$ ((i++))$ echo $i1$ let i++$ echo $i2$ expr $i + 13$ echo $i2$ echo $i 1 | awk '{printf $1+$2}'3说明: expr 之后的 $i,+,1 之间有空格分开。如果进行乘法运算,需要对运算符进行转义,否则 Shell 会把乘号解释为通配符,导致语法错误; awk 后面的 $1 和 $2 分别指 $i 和 1,即从左往右的第 1 个和第 2 个数。用 Shell 的内置命令查看各个命令的类型如下:$ type typetype is a shell builtin$ type letlet is a shell builtin$ type exprexpr is hashed (/usr/bin/expr)$ type bcbc is hashed (/usr/bin/bc)$ type awkawk is /usr/bin/awk从上述演示可看出: let 是 Shell 内置命令,其他几个是外部命令,都在 /usr/bin 目录下。而 expr 和 bc 因为刚用过,已经加载在内存的 hash 表中。这将有利于我们理解在上一章介绍的脚本多种执行方法背后的原理。说明:如果要查看不同命令的帮助,对于 let 和 type 等 Shell 内置命令,可以通过 Shell 的一个内置命令 help 来查看相关帮助,而一些外部命令可以通过 Shell 的一个外部命令 man 来查看帮助,用法诸如 help let,man expr 等。范例:从 1 加到某个数#!/bin/bash# calc.shi=0;while [ $i -lt 10000 ]do ((i++))doneecho $i说明:这里通过 while [ 条件表达式 ]; do .... done 循环来实现。-lt 是小于号 <,具体见 test 命令的用法:man test。如何执行该脚本?办法一:直接把脚本文件当成子 Shell (Bash)的一个参数传入$ bash calc.sh$ type bashbash is hashed (/bin/bash)办法二:是通过 bash 的内置命令 . 或 source 执行$ . ./calc.sh或$ source ./calc.sh$ type .. is a shell builtin$ type sourcesource is a shell builtin办法三:是修改文件为可执行,直接在当前 Shell 下执行$ chmod ./calc.sh$ ./calc.sh下面,逐一演示用其他方法计算变量加一,即把 ((i++)) 行替换成下面的某一个:let i++;i=$(expr $i + 1)i=$(echo $i+1|bc)i=$(echo "$i 1" | awk '{printf $1+$2;}')比较计算时间如下:$ time calc.sh10000real 0m1.319suser 0m1.056ssys 0m0.036s$ time calc_let.sh10000real 0m1.426suser 0m1.176ssys 0m0.032s$ time calc_expr.sh1000real 0m27.425suser 0m5.060ssys 0m14.177s$ time calc_bc.sh1000real 0m56.576suser 0m9.353ssys 0m24.618s$ time ./calc_awk.sh100real 0m11.672suser 0m2.604ssys 0m2.660s说明: time 命令可以用来统计命令执行时间,这部分时间包括总的运行时间,用户空间执行时间,内核空间执行时间,它通过 ptrace 系统调用实现。通过上述比较可以发现 (()) 的运算效率最高。而 let 作为 Shell 内置命令,效率也很高,但是 expr,bc,awk 的计算效率就比较低。所以,在 Shell 本身能够完成相关工作的情况下,建议优先使用 Shell 本身提供的功能。但是 Shell 本身无法完成的功能,比如浮点运算,所以就需要外部命令的帮助。另外,考虑到 Shell 脚本的可移植性,在性能不是很关键的情况下,不要使用某些 Shell 特有的语法。let,expr,bc 都可以用来求模,运算符都是 %,而 let 和 bc 可以用来求幂,运算符不一样,前者是 **,后者是 ^ 。例如:范例:求模$ expr 5 % 21$ let i=5%2$ echo $i1$ echo 5 % 2 | bc1$ ((i=5%2))$ echo $i1范例:求幂$ let i=5**2$ echo $i25$ ((i=5**2))$ echo $i25$ echo "5^2" | bc25范例:进制转换进制转换也是比较常用的操作,可以用 Bash 的内置支持也可以用 bc 来完成,例如把 8 进制的 11 转换为 10 进制,则可以:$ echo "obase=10;ibase=8;11" | bc -l9$ echo $((8#11))9上面都是把某个进制的数转换为 10 进制的,如果要进行任意进制之间的转换还是 bc 比较灵活,因为它可以直接用 ibase 和 obase 分别指定进制源和进制转换目标。范例:ascii 字符编码如果要把某些字符串以特定的进制表示,可以用 od 命令,例如默认的分隔符 IFS 包括空格、 TAB 以及换行,可以用 man ascii 佐证。$ echo -n "$IFS" | od -c0000000 t n0000003$ echo -n "$IFS" | od -b0000000 040 011 0120000003浮点运算let 和 expr 都无法进行浮点运算,但是 bc 和 awk 可以。范例:求 1 除以 13,保留 3 位有效数字$ echo "scale=3; 1/13" | bc.076$ echo "1 13" | awk '{printf("%0.3fn",$1/$2)}'0.077说明: bc 在进行浮点运算时需指定精度,否则默认为 0,即进行浮点运算时,默认结果只保留整

[1] [2] [3]  下一页


Shell 数值运算