shell 数组
创建数组 array_name=(ele1 ele2 ele3 ... elen)
可以为不用类型
arr=(20 56 "http://c.biancheng.net/shell/")
Shell 数组的长度不是固定的,定义之后还可以增加元素。例如,
对于上面的 nums 数组,它的长度是 6,
使用下面的代码会在最后增加一个元素,使其长度扩展到 7:
nums[6]=88
此外,你也无需逐个元素地给数组赋值,下面的代码就是只给特定元素赋值:
ages=([3]=24 [5]=19 [10]=12)
获取数组元素
${array_name[index]}
n=${nums[2]}
使用@或*可以获取数组中的所有元素,例如:
${nums[*]}
${nums[@]}
完整的演示:
#!/bin/bash
nums=(29 100 13 8 91 44)
echo ${nums[@]} #输出所有数组元素
nums[10]=66 #给第10个元素赋值(此时会增加数组长度)
echo ${nums[*]} #输出所有数组元素
echo ${nums[4]} #输出第4个元素
运行结果:
29 100 13 8 91 44
29 100 13 8 91 44 66
91
利用@或*,可以将数组扩展成列表,然后使用#来获取数组元素的个数,格式如下:
${#array_name[@]}
${#array_name[*]}
如果某个元素是字符串,还可以通过指定下标的方式获得该元素的长度,如下所示:
${#arr[2]}
下面我们通过实际代码来演示一下如何获取数组长度。
#!/bin/bash
nums=(29 100 13)
echo ${#nums[*]}
#向数组中添加元素
nums[10]="http://c.biancheng.net/shell/"
echo ${#nums[@]}
echo ${#nums[10]}
#删除数组元素
unset nums[1]
echo ${#nums[*]}
运行结果:
3
4
29
3
拼接数组的思路是:先利用@或*,将数组扩展成列表,然后再合并到一起。具体格式如下:
array_new=(${array1[@]} ${array2[@]})
array_new=(${array1[*]} ${array2[*]})
在 [Shell](http://c.biancheng.net/shell/) 中,使用 unset 关键字
来删除数组元素,具体格式如下:
unset array_name[index]
如果不写下标,而是写成下面的形式:
unset array_name
那么就是删除整个数组,所有元素都会消失。
shell内建命令
通常来说,内建命令会比外部命令执行得更快,执行外部命令时不但会触发磁盘 I/O,还需要 fork 出一个单独的进程来执行,执行完成后再退出。而执行内建命令相当于调用当前 Shell 进程的一个函数。
可以使用 type 来确定一个命令是否是内建命令:
[root@localhost ~]$ type cd
cd is a Shell builtin
[root@localhost ~]$ type ifconfig
ifconfig is /sbin/ifconfig
-
alias unalias
alias 用来给命令创建一个别名。若直接输入该命令且不带任何参数,则列出当前 Shell 环境中使用了哪些别名。现在你应该能理解类似
ll
这样的命令为什么与ls -l
的效果是一样的吧。
alias myShutdown='shutdown -h now'
使用 unalias 内建命令可以删除当前 Shell 环境中的别名。unalias 有两种使用方法:
第一种用法是在命令后跟上某个命令的别名,用于删除指定的别名。
第二种用法是在命令后接-a参数,删除当前 Shell 环境中所有的别名。
-
echo
echo 是一个 shell内建命令,用来在终端输出字符串,并在最后默认加上换行符;
echo 命令输出结束后默认会换行,如果不希望换行,可以加上-n参数;
我们可以添加-e参数来让 echo 命令解析转义字符;
有了-e参数,我们也可以使用转义字符\c来强制 echo 命令不换行了;
-
exit
exit 是一个 Shell 内置命令,用来退出当前 Shell:
- 如果在终端中直接运行 exit 命令,会退出当前登录的 Shell,并关闭终端;
- 如果在 Shell 脚本中出现 exit 命令,会停止执行后边的所有* 代码,立即退出 Shell 脚本。
- exit 命令可以接受的参数是一个状态值 n,代表退出时的状态。如果不指定,默认状态值是 0
-
ulimit
默认情况下 Linux 系统的各个资源都做了软硬限制,其中硬限制的作用是控制软限制(换言之,软限制不能高于硬限制)。使用ulimit -a
可以查看当前系统的软限制,使用命令ulimit -a –H
可查看系统的硬限制。
#core文件大小,单位是block,默认为0
core file size (blocks, -c) 0
#数据段大小,单位是kbyte,默认不做限制
data seg size (kbytes, -d) unlimited
#调度优先级,默认为0
scheduling priority (-e) 0
#创建文件的大小,单位是block,默认不做限制
file size (blocks, -f) unlimited
#挂起的信号数量,默认是8192
pending signals (-i) 8192
#最大锁定内存的值,单位是kbyte,默认是32
max locked memory (kbytes, -l) 32
#最大可用的常驻内存值,单位是kbyte,默认不做限制
max memory size (kbytes, -m) unlimited
#最大打开的文件数,默认是1024
open files (-n) 1024
#管道最大缓冲区的值
pipe size (512 bytes, -p) 8
#消息队列的最大值,单位是byte
POSIX message queues (bytes, -q) 819200
#程序的实时性优先级,默认为0
real-time priority (-r) 0
#栈大小,单位是kbyte
stack size (kbytes, -s) 10240
#最大cpu占用时间,默认不做限制
cpu time (seconds, -t) unlimited
#用户最大进程数,默认是8192
max user processes (-u) 8192
#最大虚拟内存,单位是kbyte,默认不做限制
virtual memory (kbytes, -v) unlimited
#文件锁,默认不做限制
file locks (-x) unlimited
每一行中都包含了相应的改变该项设置的参数,
以最大可以打开的文件数为例(open files 默认是 1024),
想要增大至 4096 则按照如下命令设置(可参照此方法调整其他参数)。
#设置最大打开的文件数
#该命令会同时设置硬限制和软限制
[root@localhost ~]# ulimit -n 4096
#使用-S参数单独设置软限制
#[root@localhost ~]# ulimit -S -n 4096
#使用-H参数单独设置硬限制
#[root@localhost ~]# ulimit -H -n 4096
使用 ulimit 直接调整参数,只会在当前运行时生效,一旦系统重启,所有调整过的参数就会变回系统默认值。所以建议将所有的改动放在 ulimit 的系统配置文件中。相关配置方法请参考笔者对相关配置文件的注释。
[root@localhost ~]# cat /etc/security/limits.conf
# /etc/security/limits.conf
#该文件是ulimit的配置文件,任何对系统的ulimit的修改都应该写入该文件
#请将所有的设置写到该文件的最后
#Each line describes a limit for a user in the form:
#配置应该写成下面这行的格式,即每个配置占用1行,每行4列
#每列分别是<domain> <type> <item> <value>
#<domain> <type> <item> <value>
#
#其中:
#<domain>可以取的值如下:
# - 一个用户名
# - 一个组名,组名前面用@符号
# - 通配符*
# - 通配符%
#Where:
#<domain> can be:
# - an user name
# - a group name, with @group syntax
# - the wildcard *, for default entry
# - the wildcard %, can be also used with %group syntax,
# for maxlogin limit
#
#<type>只有以下两个可用值:
# - soft用于设置软限制
# - hard用于设置硬限制
#<type> can have the two values:
# - "soft" for enforcing the soft limits
# - "hard" for enforcing hard limits
#
#<item>的值可以是以下任意一种:
# - core - core文件大小的限制 (KB)
# - data - 最大数据段限制 (KB)
# - fsize - 最大文件大小 (KB)
# - memlock - 最大锁定的内存大小 (KB)
# - nofile - 最大打开文件数
# - rss - 最大常驻内存值 (KB)
# - stack - 最大栈空间大小 (KB)
# - cpu - 最大CPU使用时间 (MIN)
# - nproc - 最大进程数
# - as - 虚拟地址空间
# - maxlogins - 某用户的最大登录数
# - maxsyslogins - 系统用户最大登录数
# - priority - 用户进程的运行优先级
# - locks – 用户最大可以锁定文件的数量
# - sigpending - 最大挂起的信号量数
# - msgqueue - POSIX信号队列使用的最大内存值 (bytes)
# - nice - 最大nice值
# - rtprio - 最大实时优先级
#
#<item> can be one of the following:
# - core - limits the core file size (KB)
# - data - max data size (KB)
# - fsize - maximum filesize (KB)
# - memlock - max locked-in-memory address space (KB)
# - nofile - max number of open files
# - rss - max resident set size (KB)
# - stack - max stack size (KB)
# - cpu - max CPU time (MIN)
# - nproc - max number of processes
# - as - address space limit
# - maxlogins - max number of logins for this user
# - maxsyslogins - max number of logins on the system
# - priority - the priority to run user process with
# - locks - max number of file locks the user can hold
# - sigpending - max number of pending signals
# - msgqueue - max memory used by POSIX message queues (bytes)
# - nice - max nice priority allowed to raise to
# - rtprio - max realtime priority
#
#<domain> <type> <item> <value>
#
#以下是使用样例,请参照配置
#* soft core 0
#* hard rss 10000
#@student hard nproc 20
#@faculty soft nproc 20
#@faculty hard nproc 50
#ftp hard nproc 0
#@student - maxlogins 4
-
declare
设置变量的属性
declare [+/-] [aAfFgilprtux] [变量名=变量值]
其中,-表示设置属性,+表示取消属性,aAfFgilprtux都是具体的选项,它们的含义如下表所示:
选项 | 含义 |
---|---|
-f [name] | 列出之前由用户在脚本中定义的函数名称和函数体。 |
-F [name] | 仅列出自定义函数名称。 |
-g name | 在 Shell 函数内部创建全局变量。 |
-p [name] | 显示指定变量的属性和值。 |
-a name | 声明变量为普通数组。 |
-A name | 声明变量为关联数组(支持索引下标为字符串) |
-i name | 将变量定义为整数型。 |
-r name[=value] | 将变量定义为只读(不可修改和删除),等价于 readonly name。 |
-x name[=value] | 将变量设置为环境变量,等价于 export name[=value]。 |
命令替换
Shell 中有两种方式可以完成命令替换,一种是反引号,一种是$(),使用方法如下:
var_name=`command`
var_name=$(command)
如果被替换的命令的输出内容包括多行(也即有换行符),
或者含有多个连续的空白符,
那么在输出变量时应该将变量用双引号包围,
否则系统会使用默认的空白符来填充,
这会导致换行无效,以及连续的空白符被压缩成一个。
请看下面的代码:
#!/bin/bash
LSL=`ls -l`
echo $LSL #不使用双引号包围
echo "--------------------------" #输出分隔符
echo "$LSL" #使用引号包围
运行结果:
total 8 drwxr-xr-x. 2 root root 21 7月 1 2016 abc -rw-rw-r--. 1 mozhiyan mozhiyan 147 10月 31 10:29 demo.sh -rw-rw-r--. 1 mozhiyan mozhiyan 35 10月 31 10:20 demo.sh~
--------------------------
total 8
drwxr-xr-x. 2 root root 21 7月 1 2016 abc
-rw-rw-r--. 1 mozhiyan mozhiyan 147 10月 31 10:29 demo.sh
-rw-rw-r--. 1 mozhiyan mozhiyan 35 10月 31 10:20 demo.sh~
原则上讲,上面提到的两种变量替换的形式是等价的,可以随意使用;但是,反引号毕竟看起来像单引号,有时候会对查看代码造成困扰,而使用$()就相对清晰,能有效避免这种混乱。而且有些情况必须使用它,支持嵌套,反引号不行。
下面的例子演示了使用计算 ls 命令列出的第一个文件的行数,这里使用了两层嵌套。
[root@localhost ~]# Fir_File_Lines=$(wc -l $(ls | sed -n '1p'))
[root@localhost ~]# echo "$Fir_File_Lines"
36 anaconda-ks.cfg
要注意的是,$() 仅在 Bash Shell 中有效,而反引号可在多种 Shell 中使用。所以这两种命令替换的方式各有特点,究竟选用哪种方式全看个人需求。
数学计算命令
运算操作符 | 说明 |
---|---|
(()) | 用于整数运算,效率很高,推荐 |
let | 用于整数运算,和(())类似 |
$[] | 用于整数运算,不如(())灵活 |
expr | 用于整数运算,也可以处理字符串,比较麻烦,不推荐 |
bc | linux下的一个计算机程序,可以处理整数和小数。推荐 |
declare -i | 将变量定义为整数,功能有限,只支持基本数学运算 不支持逻辑运算 |
-
(())
运算命令 | 说明 |
---|---|
((a=10+66)((b=a-15))((c=a+b)) | 这种写法可以在计算完成后给变量赋值。以 ((b=a-15)) 为例,即将 a-15 的运算结果赋值给变量 c。注意,使用变量时不用加$前缀,(( )) 会自动解析变量名。 |
a=$ ((10+66) b= $ ((a-15)) c=$ ((a+b)) |
可以在 (( )) 前面加上$ 符号获取 (( )) 命令的执行结果,也即获取整个表达式的值。以 c=$ ((a+b)) 为例,即将 a+b 这个表达式的运算结果赋值给变量 c。注意,类似 c=((a+b)) 这样的写法是错误的,不加$ 就不能取得表达式的结果。 |
((a>7 && b==c)) | (( )) 也可以进行逻辑运算,在 if 语句中常会使用逻辑运算。 |
echo $((a+10)) | 需要立接输出表达式的运算结果时,可以在 (( )) 前面加$ 符号。 |
((a=3+5, b=a+10)) | 对多个表达式同时进行计算。 |
-
let
注意:和双小括号 (( )) 一样,let 命令也只能进行整数运算,不能对小数(浮点数)或者字符串进行运算.
let 命令的语法格式为:
let 表达式
let "表达式"
let '表达式'
和 (( )) 类似,let 命令也支持一次性计算多个表达式,并且以最后一个表达式的值作为整个 let 命令的执行结果。但是,对于多个表达式之间的分隔符,let 和 (( )) 是有区别的:
let 命令以空格来分隔多个表达式;
(( )) 以逗号,来分隔多个表达式。
另外还要注意,对于类似let x+y这样的写法,Shell 虽然计算了 x+y 的值,但却将结果丢弃;若不想这样,可以使用let sum=x+y将 x+y 的结果保存在变量 sum 中
这种情况下 (( )) 显然更加灵活,可以使用$((x+y))来获取 x+y 的结果
-
$[]
$
[] 的用法如下:
$[表达式]
$[] 会对表达式进行计算,并取得计算结果。
如果表达式中包含了变量,那么你可以加$,也可以不加。
[c.biancheng.net]$ echo $[3*5] #直接输出结算结果
15
[c.biancheng.net]$ echo $[(3+4)*5] #使用()
35
[c.biancheng.net]$ n=6
[c.biancheng.net]$ m=$[n*2] #将计算结果赋值给变量
[c.biancheng.net]$ echo $[m+n]
18
[c.biancheng.net]$ echo $[$m*$n] #在变量前边加$也是可以的
72
[c.biancheng.net]$ echo $[4*(m+n)]
72
需要注意的是,不能单独使用 $[],
必须能够接收 $[] 的计算结果。例如,下面的用法是错误的:
[c.biancheng.net]$ $[3+4]
bash: 7: 未找到命令...
[c.biancheng.net]$ $[m+3]
bash: 15: 未找到命令...
-
expr
expr 是 evaluate expressions 的缩写,译为“表达式求值”。Shell expr 是一个功能强大,并且比较复杂的命令,它除了可以实现整数计算,还可以结合一些选项对字符串进行处理,例如计算字符串长度、字符串比较、字符串匹配、字符串提取等。
Shell expr 对于整数计算的用法为:
expr 表达式
expr 对表达式的格式有几点特殊的要求:
出现在表达式中的运算符、数字、变量和小括号的左右两边至少要有一个空格,否则会报错。
有些特殊符号必须用反斜杠\进行转义(屏蔽其特殊含义),比如乘号和小括号(),如果不用\转义,那么 Shell 会把它们误解为正则表达式中的符号(对应通配符,()对应分组)。
使用变量时要加$
前缀。
[c.biancheng.net]$ expr 2 +3 #错误:加号和 3 之前没有空格
expr: 语法错误
[c.biancheng.net]$ expr 2 + 3 #这样才是正确的
5
[c.biancheng.net]$ expr 4 * 5 #错误:乘号没有转义
expr: 语法错误
[c.biancheng.net]$ expr 4 \* 5 #使用 \ 转义后才是正确的
20
[c.biancheng.net]$ expr ( 2 + 3 ) \* 4 #小括号也需要转义
bash: 未预期的符号 `2' 附近有语法错误
[c.biancheng.net]$ expr \( 2 + 3 \) \* 4 #使用 \ 转义后才是正确的
20
[c.biancheng.net]$ n=3
[c.biancheng.net]$ expr n + 2 #使用变量时要加 $
expr: 非整数参数
[c.biancheng.net]$ expr $n + 2 #加上 $ 才是正确的
5
[c.biancheng.net]$ m=7
[c.biancheng.net]$ expr $m \* \( $n + 5 \)
56
以上是直接使用 expr 命令,计算结果会直接输出,
如果你希望将计算结果赋值给变量,
那么需要将整个表达式用反引号``(位于 Tab 键的上方)包围起来,请看下面的例子。
[c.biancheng.net]$ m=5
[c.biancheng.net]$ n=`expr $m + 10`
[c.biancheng.net]$ echo $n
15
-
bc !!!
bc 甚至可以称得上是一种编程语言了,它支持变量、数组、输入输出、分支结构、循环结构、函数等基本的编程元素,所以 Linux 手册中是这样来描述 bc 的:
An arbitrary precision calculator language
翻译过来就是“一个任意精度的计算器语言”。
从终端进入 bc
选项 | 说明 |
---|---|
-h --help | 帮助信息 |
-v --version | 显示命令版本信息 |
-l --mathlib | 使用标准数学库 |
-i --interactive | 强制交互 |
-w --warn | 显示POSIX的警告信息 |
-s --standard | 使用POSIX标准来处理 |
-q --quiet | 不显示欢迎信息 |
在交互式环境下使用bc
支持变量、函数、循环结构、分支结构
内置变量
变量名 | 作用 |
---|---|
scale | 指定精度,也既小数点后的位数;默认为0,也既不使用小数部分 |
ibase | 指定输入的数字的进制,默认是10进制 |
obase | 指定输出的数字的进制,默认为10 |
last 或者. | 表示最近打印的数字 |
内置函数
函数名 | 作用 |
---|---|
s(x) | 计算 x 的正弦值,x 是弧度值 |
c(x) | 计算 x 的余弦值,x 是弧度值。 |
a(x) | 计算 x 的反正切值,返回弧度值。 |
l(x) | 计算 x 的自然对数。 |
e(x) | 求 e 的 x 次方。 |
j(n,x) | 贝塞尔函数,计算从 n 到 x 的阶数 |
在 Shell 中使用 bc 计算器
在 Shell 脚本中,我们可以借助管道或者输入重定向来使用 bc 计算器。
- 管道是 Linux 进程间的一种通信机制,它可以将前一个命令(进程)的输出作为下一个命令(进程)的输入,两个命令之间使用竖线
|
分隔。 - 通常情况下,一个命令从终端获得用户输入的内容,如果让它从其他地方(比如文件)获得输入,那么就需要重定向。
借助管道使用 bc 计算器
如果读者希望直接输出 bc 的计算结果,那么可以使用下面的形式:
echo "expression" | bc
expression就是希望计算的数学表达式,它必须符合 bc 的语法,上面我们已经进行了介绍。在 expression 中,还可以使用 Shell 脚本中的变量。
使用下面的形式可以将 bc 的计算结果赋值给 Shell 变量:
variable=$(echo "expression" | bc)
[c.biancheng.net]$ echo "3*8"|bc
24
[c.biancheng.net]$ ret=$(echo "4+9"|bc)
[c.biancheng.net]$ echo $ret
13
使用 bc 中的变量:
[c.biancheng.net]$ echo "scale=4;3*8/7"|bc
3.4285
[c.biancheng.net]$ echo "scale=4;3*8/7;last*5"|bc
3.4285
17.1425
使用 Shell 脚本中的变量:
[c.biancheng.net]$ x=4
[c.biancheng.net]$ echo "scale=5;n=$x+2;e(n)"|bc -l
403.42879
进制转换:
#十进制转十六进制
[mozhiyan@localhost ~]$ m=31
[mozhiyan@localhost ~]$ n=$(echo "obase=16;$m"|bc)
[mozhiyan@localhost ~]$ echo $n
1F
#十六进制转十进制
[mozhiyan@localhost ~]$ m=1E
[mozhiyan@localhost ~]$ n=$(echo "obase=10;ibase=16;$m"|bc)
[mozhiyan@localhost ~]$ echo $n
30
借助输入重定向使用 bc 计算器
可以使用下面的形式将 bc 的计算结果赋值给 Shell 变量:
variable=$(bc << EOF
expressions
EOF
)
其中,variable是 Shell 变量名,
express是要计算的数学表达式(可以换行,和进入 bc 以后的书写形式一样),
EOF是数学表达式的开始和结束标识(你也可以换成其它的名字,比如 aaa、bbb 等)。
[c.biancheng.net]$ m=1E
[c.biancheng.net]$ n=$(bc << EOF
> obase=10;
> ibase=16;
> print $m
> EOF
> )
[c.biancheng.net]$ echo $n
30
如果你有大量的数学计算,那么使用输入重定向就比较方便,
因为数学表达式可以换行,写起来更加清晰明了。
-
declare -i
默认情况下,Shell 中每一个变量的值都是字符串,即使你给变量赋值一个数字,它其实也是字符串,所以在进行数学计算时会出错。
使用 declare 命令的-i选项可以将一个变量声明为整数类型,这样在进行数学计算时就不会作为字符串处理了,请看下面的例子:
#!/bin/bash
declare -i m n ret
m=10
n=30
ret=$m+$n
echo $ret
ret=$n/$m
echo $ret
运行结果:
40
3
除了将 m、n 定义为整数,还必须将 ret 定义为整数,如果不这样做,在执行ret=n和ret=m时,Shell 依然会将 m、n 视为字符串。
此外,你也不能写类似echo n这样的语句,这种情况下 m、n 也会被视为字符串。
和 (())、let、$[] 不同,declare -i的功能非常有限,仅支持最基本的数学运算(加减乘除和取余),不支持逻辑运算(比较运算、与运算、或运算、非运算),所以在实际开发中很少使用。