Shell脚本快速入门学习

常用的Shell

Shell 既是一种脚本编程语言,也是一个连接内核和用户的软件。

常见的 Shell 有 sh、bash、csh、tcsh、ash 等。

bash shell 是 Linux 的默认 shell,bash 由 GNU 组织开发,保持了对 sh shell 的兼容性,是各种 Linux 发行版默认配置的 shell。

bash 兼容 sh 意味着,针对 sh 编写的 Shell 代码可以不加修改地在 bash 中运行。

尽管如此,bash 和 sh 还是有一些不同之处:

  • 一方面,bash 扩展了一些命令和参数;
  • 另一方面,bash 并不完全和 sh 兼容,它们有些行为并不一致,但在大多数企业运维的情况下区别不大,特殊场景可以使用 bash 代替 sh。

查看Shell

Shell是一个程序,一般放在/bin/user/bin目录下,Linux系统可用的Shell都可以使用下面命令查看

 

user@PxeCtrlSys:~$ cat /etc/shells 
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash

现在Linux基本都是用的bash Shell,查看Linux的默认shell

 

user@PxeCtrlSys:~$ echo $SHELL
/bin/bash

运行方法

  • 作为可执行程序

代码保存test.sh文本中

 

# 进入脚本所在目录,让脚本拥有可执行权限
chmod +x ./test.sh
# 执行方式
./test.sh

一定要写成./test.sh,而不是test.sh,运行其它二进制的程序也一样,直接写 test.sh,linux 系统会去 PATH 里寻找有没有叫 test.sh 的,而只有 /bin, /sbin, /usr/bin/usr/sbin 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 test.sh 是会找不到命令的,要用 ./test.sh 告诉系统说,就在当前目录找。

  • 作为解释器参数

直接运行解释器,其参数就是 shell 脚本的文件名

 

/bin/sh test.sh
# 或者
/bin/bash test.sh

Shell变量

在Bash Shell中,每一个变量的值都是字符串,无论给变量赋值时有没使用引号,值都会以字符串的形式存储,即使将整数或小数赋值给变量,他们也会被视为字符串。

变量赋值

定义变量,三种定义方式,显式赋值

 

name="test"
name='test'
name=test

注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则:

  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
  • 中间不能有空格,可以使用下划线(_)。
  • 不能使用标点符号。
  • 不能使用bash里的关键字(可用help命令查看保留关键字)。

如果变量值包含空白字符,就必须使用引号包围起来,例如name="bom smith"

用语句给变量赋值

 

for file in `ls /etc`
或
for file in $(ls /etc)

/etc目录下的文件名循环出来。

使用变量

 

name="test"
echo $name
# 或者加花括号,含义一样,加花括号是为了帮助解释器识别变量的边界
echo ${name}

例如和变量连接的字符

 

#!/bin/bash
for item in Python Java Shell Bat; do
    echo "This is ${item}Script"
done

运行结果如下

 

This is PythonScript
This is JavaScript
This is ShellScript
This is BatScript

如果没有花括号,那么变量就是$itemScript,其值为空,结果就会错误。推荐加花括号{}

修改变量的值

已定义过的变量可以重新赋值

 

#!/bin/bash
name='test'
echo ${name}  # test
name="new"
echo $name  # new

变量赋值不能加$符号,只有在使用键盘时才能加$

命令执行结果赋值给变量

支持将命令的执行结果赋值给变量使用,常见的两种方式:

 

value=`command`
# 或
value=$(command)

# 默认输出以空白符填充
echo $value
# 按照原格式输出,例如有换行符的
echo "$value"

第一种是使用反引号包围起来,第二种是使用$()包围起来(推荐使用第二种方式)。command是执行命令。

 

user@PxeCtrlSys:~$ res=`who`
user@PxeCtrlSys:~$ echo $res
user pts/0 2019-03-11 09:04 (192.168.96.16) user pts/1 2019-03-11 09:04 (192.168.96.16)
user@PxeCtrlSys:~$ res=$(ls /)
user@PxeCtrlSys:~$ echo $res
bin boot dev etc home initrd.img lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var vmlinuz

执行结果赋值给变量,输出不会自动添加换行符。

如果被替换的命令的输出内容包括多行(也即有换行符),或者含有多个连续的空白符,那么在输出变量时应该将变量用双引号包围,否则系统会使用默认的空白符来填充,这会导致换行无效,以及连续的空白符被压缩成一个。请看下面的代码:

 

user@PxeCtrlSys:~$ res=`who`
# 不用引号包围变量,输出不会自动换行
user@PxeCtrlSys:~$ echo $res
user pts/0 2019-03-11 09:04 (192.168.96.16) user pts/1 2019-03-11 09:04 (192.168.96.16)
# 使用引号包围变量,输出按照原来的格式
user@PxeCtrlSys:~$ echo "$res"
user     pts/0        2019-03-11 09:04 (192.168.96.16)
user     pts/1        2019-03-11 09:04 (192.168.96.16)

为了防止出现格式混乱的情况,建议在输出变量时加上双引号。

只读变量

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
下面的例子尝试更改只读变量,结果报错:

 

#!/bin/bash
name='test'
readonly name
name='new'
echo $name

运行结果

 

test
/tmp/280278210/main.sh: line 4: name: readonly variable
# 当改为只读后,再次赋值就会报错

删除变量

使用 unset 命令可以删除变量

 

#!/bin/bash
name='test'
unset name
echo $name  # 执行没有输出

变量删除后不能再次使用。unset命令不能删除只读变量。

变量类型,作用域

运行shell时,会同时存在三种变量:

  1. 全局变量 指变量在当前的整个 Shell 会话中都有效。每个 Shell 会话都有自己的作用域,彼此之间互不影响。在 Shell 中定义的变量,默认就是全局变量。
  2. 局部变量 在 Shell 函数中定义的变量默认也是全局变量,它和在函数外部定义变量拥有一样的效果;要想变量的作用域仅限于函数内部,那么可以在定义时加上local命令,此时该变量就成了局部变量。
  3. 环境变量 只在当前 Shell 会话中有效,如果使用export命令将它导出,那么它就在所有的子 Shell 中也有效了。

Shell字符串

字符串最常用的数据类型,可以用单/双引号,也可以不用引号。

单引号

 

str='this is a string'

单引号字符串的限制:

  • 单引号里面的任何字符都会原样输出,单引号字符串里面的变量是无效的,也就是变量那一串就只是字符串;
  • 单引号字符中不能出现单独一个的单引号(对单引号使用转义符后也不行),但能成对出现,相当于字符串拼接s='abc\'0e',这个就会报错,而s='abc\'0'e'会输出abc\0e

双引号

  • 双引号中可以有变量,输出时会先解析里面的变量和命令
  • 双引号也可以出现转义字符

 

#!/bin/bash
name='lr'
echo $name
s1="my name is $name"
echo $s1
s2="my name is "$name""
echo $s2
s3="my name is \"$name\""
echo $s3

# 运行结果
lr
my name is lr
my name is lr
my name is "lr"

单/双引号使用场景

建议:

  • 如果变量的值是数字,那么不加引号a=1
  • 如果要将字符串原样输出,就用反引号str='单引号中原样输出${test}'
  • 没有特别要求最好都是用双引号(最常见)。

拼接字符串

字符串的拼接(也称字符串连接或者字符串合并),在 Shell 中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接。

 

#!/bin/bash

name='lr'

s1="hello, $name"  # 双引号中可以直接使用变量
echo $s1
s2="hello, "$name""  # 相当于两对双引号拼接
echo $s2
s3='hello, $name'  # 单引号中变量都当作普通字符串
echo $s3
s4='hello, "$name"'
echo $s4
s5='hello, '$name''  # 相当于两对单引号和变量进行拼接
echo $s5

nick='xy'
s6=$nick$name  # 中间不能有空格
s7="$nick $name"  # 如果是双引号包围,就允许中间有空格
s8=$nick"..."$name  # 中间可以出现其他字符串
s9="$nick...$name"  # 中间也可以这样写
s10="昵称${nick}...${name}名字"

echo $s6 
echo $s7 
echo $s8 
echo $s9 
echo $s10


# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
hello, lr
hello, lr
hello, $name
hello, "$name"
hello, lr

xylr
xy lr
xy...lr
xy...lr
昵称xy...lr名字

获取字符串长度

使用${#变量名}获取长度

 

#!/bin/bash
name='lr'
echo ${#name}

# 运行结果
2

截取字符串

Shell 截取字符串通常有两种方式:从指定位置开始截取和从指定字符(子字符串)开始截取。

从指定位置开始截取

种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串。

从字符串左边开始计数,格式为${string: start :length}

 

#!/bin/bash
str="I love Python, but I need study Shell"
echo ${str:2:7}  # 下标以0开始,从第2个开始截取,共截取7个字符
echo ${str:19}  # 省略长度,直接从指定位置截取到最后


# 运行结果
love Py
I need study Shell

从右边开始计数,格式为${string: 0-start :length}

多了0-,这是固定的写法,专门用来表示从字符串右边开始计数。

  • 从左边开始计数时,起始数字是 0(这符合程序员思维);从右边开始计数时,起始数字是 1(这符合常人思维)。计数方向不同,起始数字也不同。
  • 不管从哪边开始计数,截取方向都是从左到右。

 

echo ${str:0-5:2}  # 从右向左数第5个,长度为2的字符串
echo ${str:0-5}  # 省略长度,直接从指定位置截取到最后


# 运行结果
Sh
Shell

从指定字符(子字符串)开始截取

这种截取方式无法指定字符串长度,只能从指定字符(子字符串)截取到字符串末尾。Shell 可以截取指定字符(子字符串)右边的所有字符,也可以截取左边的所有字符。

使用 # 号截取右边字符,格式为${string#*chars}

其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),*是通配符的一种,表示任意长度的字符串。*chars连起来使用的意思是:忽略左边的所有字符,直到遇见 charschars 不会被截取)。

 

#!/bin/bash

str="I love Python, but I need study Shell"
echo ${str#*,}  # 以 , 分割,取右边

echo ${str#*Python,}  # 以Python , 分割取右边

echo ${str#I love}  # 如果不需要忽略左边的字符,那么也可以不写*

echo ${str#*o}  # 以o分割,因为字符串有好两个o,那么遇到第一个就结束了

echo ${str##*o}  # 使用两个#,就可以匹配到最后一个指定字符(子字符串)右方内容

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
but I need study Shell
but I need study Shell
Python, but I need study Shell
ve Python, but I need study Shell
n, but I need study Shell

如果希望直到最后一个指定字符(子字符串)再匹配结束,那么可以使用##,具体格式为:${string##*chars}

使用 % 截取左边字符,格式为${string%chars*}

 

#!/bin/bash

str="I love Python, but I need study Shell"
echo ${str%,*}  # 以,分割,取左边

echo ${str%Python,*}  # 以Python,分割取左边

echo ${str%Shell}  # 如果不需要忽略右边的字符,那么也可以不写*

echo ${str%o*}  # 以o分割,因为字符串有好两个o,从右往左,那么遇到第一个就结束了

echo ${str%%o*}  # 使用两个%%,就可以匹配到从右往左的最后一个指定字符(子字符串)左方内容


# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
I love Python
I love
I love Python, but I need study
I love Pyth
I l

截取字符串汇总

格式说明
${string: start :length}string 字符串的左边索引为 start 的字符开始,向右截取 length 个字符。
${string: start}string 字符串的左边索引为 start 的字符开始截取,直到最后。
${string: 0-start :length}string 字符串的右边第 start 个字符开始,向右截取 length 个字符。
${string: 0-start}string 字符串的右边第 start 个字符开始截取,直到最后。
${string#*chars}从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
${string##*chars}从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
${string%*chars}从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。
${string%%*chars}string 字符串最后一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。

查找字符串

 

#!/bin/bash
str="I love Python, but I need study Shell"
echo `expr index "$str" SP`  # 查找字符S或者P最先出现的位置,下标以1开始,第8个字符为P,就输入了8

# 运行结果
8

以上脚本中 ` 是反引号,而不是单引号 '

Shell数组

bash支持一维数组(但不支持多维数组),并且没有限制数组大小。数组元素的下标从0开始,获取数组元素使用小标,下标可以试整数或算术表达式,其值应大于等于0。

定义数组

用括号表示数组,数组元素用空格符号分隔开。

 

数组名=(值1 值2 值3 ... 值n)

或者

 

array_name=(
value0
value1
...
valuen
)

还可以单独定义数组的各个分量

 

array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen

可以不使用连续的下标,而且下标的范围没有限制

 

array=([2]=12 [5]=99)

echo ${array[0]}  # 为空值
echo ${array[2]}  # 输出值为12

array[0]=5  # 赋值一个
echo ${array[0]}  # 输出值为5

# 获取数组的所有元素
echo ${array[*]}
5 12 99
# 获取数组长度
echo ${#array[*]}
3

读取数组

一般格式

 

${数组名[下标]}

例如valuen=${array_name[n]},输出数组所有元素用echo ${array_name[@]}

 

#!/bin/bash
arr[0]=1
arr[2]=2
echo ${arr[@]}  # 使用 @ 符号可以获取数组中的所有元素:  1 2
echo ${arr[2]}  # 2
echo ${arr[1]}  # 没有的值获取为空

使用@*可以获取数组中的所有元素。

获取数组的长度

所谓数组长度,就是数组元素的个数。

利用@*,可以将数组扩展成列表,然后使用#来获取数组元素的个数,格式为 ${#array_name[@]}${#array_name[*]} ,其中array_name 表示数组名。两种形式是等价的。

 

#!/bin/bash
arr=(1 2 3 "abc")
echo ${#arr[@]}  # 获取数组的长度:4
echo ${#arr[*]}  # *也可以:4
echo ${#arr[3]}  # 获取数组中单独元素的长度:3

获取数组中元素为字符串的长度${#arr[2]}(假设下标为2的元素为字符串),因为获取字符串长度的方法是 ${#string_name}

删除数组的元素或数组

使用 unset arr[index] 删除数组元素,使用 unset arr 删除整个数组,所有元素都会消失。

 

#!/bin/bash

arr=(1 2 3 "abc")
echo ${#arr[@]}  # 获取数组的长度:4
echo 原来数组的长度为:${#arr[*]}  # *也可以:4
echo ${#arr[3]}  # 获取数组中单独元素的长度:3

unset arr[1]
echo "删除第二个元素后长度为:${#arr[*]}"

# 运行结果
user@PxeCtrlSys:~$ ./test.sh
4
原来数组的长度为:4
3
删除第二个元素后长度为:3

数组拼接、合并

将两个数组连接成一个数组。

拼接数组的思路是:先利用@或*,将数组扩展成列表,然后再合并到一起。格式为 array_new=(${array1[@]} ${array2[@]})array_new=(${array1[*]} ${array2[*]})

 

#!/bin/bash

arr1=(1 2 3 "abc")
arr2=(66)

echo "数组1:${arr1[*]}"
echo "数组2:${arr2[@]}"

new_arr=(${arr1[*]} ${arr2[@]})

echo "合并后的数组为:${new_arr[*]}"

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
数组1:1 2 3 abc
数组2:66
合并后的数组为:1 2 3 abc 66

Shell注释

#开始的行就是注释,会被解释器忽略

通过每一行添加一个#来设置多行注释

如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?

每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。

多行注释

 

:<<EOF
注释内容...
注释内容...
注释内容...
EOF

EOF也可以使用其他符号

 

:<<'
注释内容...
注释内容...
注释内容...
'

:<<!
注释内容...
注释内容...
注释内容...
!

Shell脚本传递参数

想脚本传递参数,脚本内获取参数的格式为:$nn代表一个数字,1为执行脚本的第一个参数,2为执行脚本的第二个参数,以此类推...

给脚本文件传递参数

向脚本传递3个参数,其中$0表示执行的文件名

 

user@PxeCtrlSys:~$ vim test.sh
user@PxeCtrlSys:~$ chmod +x test.sh  # 添加执行权限
user@PxeCtrlSys:~$ ./test.sh 
执行的文件名:./test.sh
传递的第一个参数:
传递的第二个参数:
传递的第三个参数:
所有参数:

user@PxeCtrlSys:~$ ./test.sh  1 2
执行的文件名:./test.sh
传递的第一个参数:1
传递的第二个参数:2
传递的第三个参数:
所有参数:1 2

user@PxeCtrlSys:~$ ./test.sh  1 2 3
执行的文件名:./test.sh
传递的第一个参数:1
传递的第二个参数:2
传递的第三个参数:3
所有参数:1 2 3

 

# test.sh文件内容
#!/bin/bash

echo "执行的文件名:$0"
echo "传递的第一个参数:$1"
echo "传递的第二个参数:$2"
echo "传递的第三个参数:$3"
echo "所有参数:$*"

如果参数个数太多,达到或者超过了 10 个,那么就得用${n}的形式来接收了,例如 ${10}${23}{ }的作用是为了帮助解释器识别参数的边界,这跟使用变量时加{ }是一样的效果。

特殊字符处理参数(特殊变量)

参数处理说明
$#获取传递到脚本的参数个数
$*以一个单字符串显示所有向脚本传递的参数,如$*就和"$1 $2 $3 ... $n"输出形式一样
$$脚本运行的当前ID号
$!后台运行的最后一个进程的ID号
$@$*相同,但是使用时加括号,斌仔引号中返回每个参数,"$1" "$2" … "$n"形式
$-显示Shell使用的当前选项,与set命令功能相同
$?显示最后命令的退出状态。0表示没有错误,其他任何值表示有错误

@ 的区别

  • 相同点:都是引用所有参数
  • 不同点:只有在双引号中提现出来。假设在脚本运行时写了三个参数1、2、3,则*等价于"1 2 3"(传递了一个参数:会将所有的参数从整体上看做一份数据,而不是把每个参数都看做一份数据。),而@等价于"1" "2" "3"(仍然将每个参数都看作一份数据,彼此之间是独立的)

 

#!/bin/bash

echo "执行的文件名:$0"
echo "传递的第一个参数:$1"
echo "传递的第二个参数:$2"
echo "传递的第三个参数:$3"

echo "\$*演示"
for i in "$*";do
    echo $i
done

echo "\$@演示"
for j in $@;do
    echo $j
done

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 1 2 3
执行的文件名:./test.sh
传递的第一个参数:1
传递的第二个参数:2
传递的第三个参数:3
$*演示  # 只循环了一次,因为$*当作一个参数
1 2 3
$@演示  # 循环多次,$@当作多个参数
1
2
3

Shell函数

shell函数格式

 

[ function ] funname [()]

{

    action;

    [return int;]

}
  • 可以带function func()定义,也可以直接func()定义,不带任何参数。
  • 参数返回,可以显示加:return返回,如果不加,将以最后一条命令运行结果,作为返回值。return后跟数值n(0-255)

无返回值函数

 

#!/bin/bash

func(){
    echo "这是一个函数中的内容" 
}

echo "开始调用函数"
func
echo "调用函数完成"

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
开始调用函数
这是一个函数中的内容
调用函数完成

有返回值函数

定义一个带return的函数

 

#!/bin/bash

func(){
    echo "请输入两个数,执行加法"
    echo -n "请输入第一个数:"
    read num1
    echo -n "请输入第二个数:"
    read num2
    # return $[ $num1 + $num2]
    # 下方表达式也正确
    return $(( $num1 + $num2 ))
}

echo "开始调用函数"
func
echo "函数返回值为 $?"
echo "调用函数完成"

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
开始调用函数
请输入两个数,执行加法
请输入第一个数:2
请输入第二个数:3
函数返回值为 5
调用函数完成

函数返回值在调用该函数后通过 $? 来获得。

所有函数在使用前必须定义。这就是说必须将函数放在脚本开始部分,直到shell解释器首次发现它时才可以使用。调用函数仅使用其函数名即可。

$? 使用方法:返回值或退出状态

$?用来获取函数返回值或者上一个命令的退出状态

每一条 Shell 命令,不管是 Bash 内置命令(例如 testecho),还是外部的 Linux 命令(例如 cdls),还是自定义的 Shell 函数,当它退出(运行结束)时,都会返回一个比较小的整数值给调用(使用)它的程序,这就是命令的退出状态(exit status)。

if 语句的判断条件,从本质上讲,判断的就是命令的退出状态。

$? 获取上一次命令退出状态

退出状态,就是上一个命令执行后的返回结果。退出状态是一个数字,一般情况下,大部分命令执行成功会返回 0,失败返回 1,这和C语言的 main() 函数是类似的。

不过,也有一些命令返回其他值,表示不同类型的错误。

 

#!/bin/bash

echo -n 请输入a:
read a
echo -n 请输入b:
read b

(( $a == $b ));
echo "退出状态:$?"

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
请输入a:1
请输入b:2
退出状态:1
user@PxeCtrlSys:~$ echo $?
0
user@PxeCtrlSys:~$ ./test.sh 
请输入a:6
请输入b:6
退出状态:0
user@PxeCtrlSys:~$ echo $?
0

$? 获取函数的返回值

给函数传递参数

Shell调用函数也可以向其传递参数。在函数体内部,通过 $n 的形式来传递参数。例如$1表示第一个参数。$2表示第二次参数

 

#!/bin/bash

func(){
    echo "第一个参数:$1"
    echo "第二个参数:$2"
    echo "第二个参数:${2}"
    echo "第五个参数:$5"
    echo "第10个参数:$10"  # 这个相当于第一个参数$1连接一个0
    echo "第10个参数:${10}"
    echo "第11个参数:$11"  # 相当于第一个参数$1连接一个1
    echo "第11个参数:${11}"
    echo "参数总数有 $# 个"
    echo "作为一个地府传输出所有参数:$*"
}

echo "开始调用函数"
func 0 2 3 4 5 23 36 65 99 123
echo "函数返回值为 $?"
echo "调用函数完成"

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
开始调用函数
第一个参数:0
第二个参数:2
第二个参数:2
第五个参数:5
第10个参数:00
第10个参数:123
第11个参数:01
第11个参数:
参数总数有 10 个
作为一个地府传输出所有参数:0 2 3 4 5 23 36 65 99 123
函数返回值为 0
调用函数完成

$10不能获取到第10个参数,正确用法是${10}n>=10时,需要使用${n}来获取参数

处理参数的特殊字符

参数处理说明
$#传递到脚本的参数个数
$*以一个单字符串显示所有向脚本传递的参数
$$脚本运行的当前进程ID号
$!后台运行的最后一个进程的ID号
$@与$*相同,但是使用时加引号,并在引号中返回每个参数。
$-显示Shell使用的当前选项,与set命令功能相同。
$?显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

Shell基本运算符

支持的运算符

  • 算术运算符
  • 关系运算符
  • 布尔运算符
  • 字符串运算符
  • 文件测试运算符

原生bash不支持简单的数学运算,但可以使用其他命令实现,例如awkexpr(最常用)

expr是一款表达式计算工具,使用它完成表达式的求值操作

例如,两个数量价,注意是反引号,而不是单引号

 

#!/bin/bash


val=`expr 2+2`
echo "求值结果:$val"
val=`expr 2 + 2`
echo "求值结果:$val"

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
求值结果:2+2
求值结果:4
  • 表达式和运算符之间要用空格,例如2+2是不正确的,需要写成2 + 2,这与大多数编程语言不同
  • 完整的表达式要被两个反引号` `包含.

算术运算符

两个数直接加不会进行算术运算

Shell 和其它编程语言不同,Shell 不能直接进行算数运算,必须使用数学计算命令。

默认情况下,Shell 不会直接进行算术运算,而是把+两边的数据(数值或者变量)当做字符串,把+当做字符串连接符,最终的结果是把两个字符串拼接在一起形成一个新的字符串。这是因为,在Shell如果不特别知名,每一个变量都是字符串,无论赋值的时候有没使用引号,值都会以字符串形式存储,默认情况下不区分变量类型。

Shell expr:进行整数计算

 

a 不等于 b
#!/bin/bash

a=2
b=3

echo `expr $a + $b`
echo `expr $a - $b`
echo `expr $a \* $b`  # 做乘法需要添加斜杠转义
echo `expr $b / $a`
echo `expr $b % $a`

c=$b
echo "赋值后c的值:$c"

if [ $a == $b ]
then
    echo "a 等于 b"
else
    echo "a 不等于 b"
fi

if [ $b == $c ]
then
    echo "b 等于 c"
fi

if [ $a != $b ]
then
    echo "a 不等于 b"
fi

if [ $c != $b ]
then
    echo "c 不等于 b"
fi

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
5
-1
6
1
1
赋值后c的值:3
a 不等于 b
b 等于 c
a 不等于 b
运算符说明 (例如a=2、b=3)举例
+加法`expr b` 结果为 30。
-减法`expr b` 结果为 -10。
*乘法`expr b` 结果为 200。
/除法`expr a` 结果为 2。
%取余`expr a` 结果为 0。
=赋值a=$b 将把变量 b 的值赋给 a。
==相等。用于比较两个数字,相同则返回 true。[ b ] 返回 false。
!=不相等。用于比较两个数字,不相同则返回 true。[ b ] 返回 true。
  • 条件表达式要放在方括号之间,并且要有空格,例如: [$a==$b] 是错误的,必须写成 [ $a == $b ]
  • 乘号(*)前边必须加反斜杠(\*)才能实现乘法运算;
  • if...then...fi 是条件语句
  • 在 MAC 中 shell 的 expr 语法是:$((表达式)),此处表达式中的 "*" 不需要转义符号 ""

数学计算命令

Shell 中常用的六种数学计算方式

运算操作符/运算命令说明
(())用于整数运算,效率很高,推荐使用
let用于整数运算,和 (()) 类似。
[$[]用于整数运算,不如 (()) 灵活。
expr可用于整数运算,也可以处理字符串。比较麻烦,需要注意各种细节,不推荐使用。
bcLinux下的一个计算器程序,可以处理整数和小数。Shell 本身只支持整数运算,想计算小数就得使用 bc 这个外部的计算器。
declare -i将变量定义为整数,然后再进行数学运算时就不会被当做字符串了。功能有限,仅支持最基本的数学运算(加减乘除和取余),不支持逻辑运算、自增自减等,所以在实际开发中很少使用。

Shell (()):对整数进行数学运算

(( )) 只能进行整数运算,不能对小数(浮点数)或者字符串进行运算。语法格式 ((表达式)) ,就是将数学运算表达式放在(())之间。

表达式可以只有一个,也可以有多个,多个表达式之间以逗号,分隔。对于多个表达式的情况,以最后一个表达式的值作为整个 (( )) 命令的执行结果。

可以使用$获取 (( )) 命令的结果,这和使用$获得变量值是类似的。

运算操作符/运算命令说明
((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))对多个表达式同时进行计算。

(( )) 中使用变量无需加上$前缀,(( )) 会自动解析变量名,这使得代码更加简洁,也符合程序员的书写习惯。

算术运算

 

# 直接输出运算结果
user@PxeCtrlSys:~$ echo $((1+1))
2
user@PxeCtrlSys:~$ echo $((2*3))
6
# 计算完成后,给变量赋值
user@PxeCtrlSys:~$ i=5
user@PxeCtrlSys:~$ ((i=i*2))
user@PxeCtrlSys:~$ echo $i
10
user@PxeCtrlSys:~$ ((i*=2))  # 简写,等效于 ((i=i*2))
user@PxeCtrlSys:~$ echo $i
20
# 复杂运算,结果赋值给变量a,变量在括号内
user@PxeCtrlSys:~$ ((a=2-5*2/4+2**2))
user@PxeCtrlSys:~$ echo $a
4
# 运算结果赋值给变量b,变量b在括号外,需要使用$
user@PxeCtrlSys:~$ b=$((2-5*2/4+2**2))
user@PxeCtrlSys:~$ echo $b
4
# 直接输出表达式的值,$符号不能去掉
user@PxeCtrlSys:~$ echo $((2-5*2/4+2**2))
4
# 利用公式计算1-100的和
user@PxeCtrlSys:~$ echo $((100*(100+1)/2))
5050

逻辑运算

 

# 结果为真,输出1,1表示真
user@PxeCtrlSys:~$ echo $((3<5))
1
user@PxeCtrlSys:~$ echo $((3>5))
0
user@PxeCtrlSys:~$ echo $((3==2+1))
1
# 多条件成立
user@PxeCtrlSys:~$ if ((8>6&&5==2+3))
> then
> echo yes
> fi
yes

(())进行自增++和自减--运算

 

user@PxeCtrlSys:~$ a=10
# ++在后面,先输出a的值,在自增
user@PxeCtrlSys:~$ echo $((a++))
10
user@PxeCtrlSys:~$ echo $a
11
# --在后面,先输出a的值,再自减
user@PxeCtrlSys:~$ echo $((a--))
11
user@PxeCtrlSys:~$ echo $a
10
# --在前面,先自减,再输出a的值
user@PxeCtrlSys:~$ echo $((--a))
9
user@PxeCtrlSys:~$ echo $a
9
# ++在前面,先自增,再输出a的值
user@PxeCtrlSys:~$ echo $((++a))
10
user@PxeCtrlSys:~$ echo $a
10

多个表达式计算

 

# 先计算第一个表达式,再计算第二个表达式
user@PxeCtrlSys:~$ ((a=3*2, b=a+6))
user@PxeCtrlSys:~$ echo $a $b
6 12
# 以最后一个表达式的结果作为整个(())命令的执行结果
user@PxeCtrlSys:~$ c=$((1+2, a+b))
user@PxeCtrlSys:~$ echo $c
18

Shell let:对整数进行数学运算

和双小括号 (( )) 一样,let 命令也只能进行整数运算,不能对小数(浮点数)或者字符串进行运算。

语法格式 let 表达式let "表达式"let '表达式',都等价于 ((表达式))

当表达式中含有 Shell 特殊字符(例如 |)时,需要用双引号" "或者单引号' '将表达式包围起来。

(( )) 类似,let 命令也支持一次性计算多个表达式,并且以最后一个表达式的值作为整个 let 命令的执行结果。但是,对于多个表达式之间的分隔符,let(( )) 是有区别的:

  • let 命令以空格来分隔多个表达式;
  • (( )) 以逗号,来分隔多个表达式。

 

user@PxeCtrlSys:~$ i=2
user@PxeCtrlSys:~$ let i+=3
user@PxeCtrlSys:~$ echo $i
5

let i+=3等价于((i+=3)),但后者效率更高。

let后面可以跟多个表达式,用空格分隔

 

user@PxeCtrlSys:~$ a=3
user@PxeCtrlSys:~$ let b=3**2 c=a+b
user@PxeCtrlSys:~$ echo $a $b
3 9
user@PxeCtrlSys:~$ echo $c
12

Shell $[]:对整数进行数学运算

(())let 命令类似,$[] 也只能进行整数运算。语法为 $[表达式]

$[] 会对表达式进行计算,并取得计算结果。如果表达式中包含了变量,那么你可以加$,也可以不加。

 

# 直接输出结果
user@PxeCtrlSys:~$ echo $[2*3]
6
user@PxeCtrlSys:~$ echo $[(2+3)/3]
1
user@PxeCtrlSys:~$ echo $[(2+3)%3]
2
user@PxeCtrlSys:~$ a=6
# 将结果赋值给变量
user@PxeCtrlSys:~$ b=$[a*2]
user@PxeCtrlSys:~$ echo $b
12
user@PxeCtrlSys:~$ echo $[a+b]
18
# 变量前加$对结果没有影响
user@PxeCtrlSys:~$ echo $[$a+$b]
18

Shell declare -i:将变量声明为整数

默认情况下,Shell每一个变量的值都是一个字符串,即使给变量赋值一个数字,它也是字符串。

使用 declare 命令的-i选项可以将一个变量声明为整数类型,这样在进行数学计算时就不会作为字符串处理了。

 

#!/bin/bash

echo 定义之前,直接求两个数的和
m=2
n=3
echo $m+$n
echo 求和后赋值给一个变量
ret=$m+$n
echo $ret

echo -e "\n声明变量为整数"
declare -i m n ret
m=2
n=3
echo 直接输出声明后的求和
echo $m+$n

ret=$m+$n
echo 求和后赋值变量
echo $ret


# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
定义之前,直接求两个数的和
2+3
求和后赋值给一个变量
2+3

声明变量为整数
直接输出声明后的求和
2+3
求和后赋值变量
5

除了将 mn 定义为整数,还必须将 ret 定义为整数,如果不这样做,在执行ret=$m+$n时,Shell 依然会将 mn 视为字符串。

此外,也不能写类似echo $m+$n这样的语句,这种情况下 mn 也会被视为字符串。

总之,除了将参与运算的变量定义为整数,还得将承载结果的变量定义为整数,而且只能用整数类型的变量来承载运算结果,不能直接使用 echo 输出。

(())let$[] 不同,declare -i的功能非常有限,仅支持最基本的数学运算(加减乘除和取余),不支持逻辑运算(比较运算、与运算、或运算、非运算),所以在实际开发中很少使用。

关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字

运算符说明(例如a=2、b=3)举例
-eq等于检测两个数是否相等,相等返回 true。[ b ] 返回 false。
-ne不等于检测两个数是否不相等,不相等返回 true。[ b ] 返回 true。
-gt大于检测左边的数是否大于右边的,如果是,则返回 true。[ b ] 返回 false。
-lt小于检测左边的数是否小于右边的,如果是,则返回 true。[ b ] 返回 true。
-ge大等于检测左边的数是否大于等于右边的,如果是,则返回 true。[ b ] 返回 false。
-le小等于检测左边的数是否小于等于右边的,如果是,则返回 true。[ b ] 返回 true。

 

#!/bin/bash

a=2
b=3

if [ $a -eq $b ]
then
    echo "a 大于 b"
else
    echo "a 小于 b"
fi


# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
a 小于 b

布尔运算符

运算符说明(例如a=2、b=3)举例
!非运算,表达式为 true 则返回 false,否则返回 true。[ ! false ] 返回 true。
-o或运算,有一个表达式为 true 则返回 true。[ b -gt 100 ] 返回 true。
-a与运算,两个表达式都为 true 才返回 true。[ b -gt 100 ] 返回 false。

 

2 小于 5 或 3 大于 100 : 返回 true
#!/bin/bash

a=2
b=3

if [ $a != $b ]
then
   echo "$a != $b : a 不等于 b"
else
   echo "$a != $b: a 等于 b"
fi
if [ $a -lt 100 -a $b -gt 15 ]
then
   echo "$a 小于 100 且 $b 大于 15 : 返回 true"
else
   echo "$a 小于 100 且 $b 大于 15 : 返回 false"
fi
if [ $a -lt 100 -o $b -gt 100 ]
then
   echo "$a 小于 100 或 $b 大于 100 : 返回 true"
else
   echo "$a 小于 100 或 $b 大于 100 : 返回 false"
fi
if [ $a -lt 5 -o $b -gt 100 ]
then
   echo "$a 小于 5 或 $b 大于 100 : 返回 true"
else
   echo "$a 小于 5 或 $b 大于 100 : 返回 false"
fi


# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
2 != 3 : a 不等于 b
2 小于 100 且 3 大于 15 : 返回 false
2 小于 100 或 3 大于 100 : 返回 true
2 小于 5 或 3 大于 100 : 返回 true

逻辑运算符

运算符说明举例
&&逻辑的 AND[[ $a -lt 100 && $b -gt 100 ]] 返回 false
` `逻辑的OR`[[ $a -lt 100 $b -gt 100 ]]` 返回 true

 

#!/bin/bash

a=2
b=3

if [[ $a -lt 5 && $b -gt 2 ]]
then
    echo "返回true"
else
    echo "返回false"
fi


if [[ $a -ge 2 || $b -le 3 ]]
then
    echo "返回true"
else
    echo "返回false"
fi


# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
返回true
返回true

字符串运算符

运算符说明举例
=检测两个字符串是否相等,相等返回 true。[ b ] 返回 false。
!=检测两个字符串是否相等,不相等返回 true。[ b ] 返回 true。
-z检测字符串长度是否为0,为0返回 true。[ -z $a ] 返回 false。
-n检测字符串长度是否为0,不为0返回 true。[ -n "$a" ] 返回 true。
$检测字符串是否为空,不为空返回 true。[ $a ] 返回 true。

 

#!/bin/bash

a="abc"
b="efg"

if [ $a = $b ]
then
   echo "$a = $b : a 等于 b"
else
   echo "$a = $b: a 不等于 b"
fi
if [ $a != $b ]
then
   echo "$a != $b : a 不等于 b"
else
   echo "$a != $b: a 等于 b"
fi
if [ -z $a ]
then
   echo "-z $a : 字符串长度为 0"
else
   echo "-z $a : 字符串长度不为 0"
fi
if [ -n "$a" ]
then
   echo "-n $a : 字符串长度不为 0"
else
   echo "-n $a : 字符串长度为 0"
fi
if [ $a ]
then
   echo "$a : 字符串不为空"
else
   echo "$a : 字符串为空"
fi

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
abc = efg: a 不等于 b
abc != efg : a 不等于 b
-z abc : 字符串长度不为 0
-n abc : 字符串长度不为 0
abc : 字符串不为空

文件测试运算符

文件测试运算符用于检测Unix文件的各种属性

操作符说明举例
-b file检测文件是否是块设备文件,如果是,则返回 true。[ -b $file ] 返回 false。
-c file检测文件是否是字符设备文件,如果是,则返回 true。[ -c $file ] 返回 false。
-d file检测文件是否是目录,如果是,则返回 true。[ -d $file ] 返回 false。
-f file检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。[ -f $file ] 返回 true。
-g file检测文件是否设置了 SGID 位,如果是,则返回 true。[ -g $file ] 返回 false。
-k file检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。[ -k $file ] 返回 false。
-p file检测文件是否是有名管道,如果是,则返回 true。[ -p $file ] 返回 false。
-u file检测文件是否设置了 SUID 位,如果是,则返回 true。[ -u $file ] 返回 false。
-r file检测文件是否可读,如果是,则返回 true。[ -r $file ] 返回 true。
-w file检测文件是否可写,如果是,则返回 true。[ -w $file ] 返回 true。
-x file检测文件是否可执行,如果是,则返回 true。[ -x $file ] 返回 true。
-s file检测文件是否为空(文件大小是否大于0),不为空返回 true。[ -s $file ] 返回 true。
-e file检测文件(包括目录)是否存在,如果是,则返回 true。[ -e $file ] 返回 true。

变量 file 表示文件"/home/user/test.sh",它的大小为100字节,具有 rwx 权限。下面的代码,将检测该文件的各种属性:

 

#!/bin/bash

file="/home/user/test.sh"
if [ -r $file ]
then
   echo "文件可读"
else
   echo "文件不可读"
fi
if [ -w $file ]
then
   echo "文件可写"
else
   echo "文件不可写"
fi
if [ -x $file ]
then
   echo "文件可执行"
else
   echo "文件不可执行"
fi
if [ -f $file ]
then
   echo "文件为普通文件"
else
   echo "文件为特殊文件"
fi
if [ -d $file ]
then
   echo "文件是个目录"
else
   echo "文件不是个目录"
fi
if [ -s $file ]
then
   echo "文件不为空"
else
   echo "文件为空"
fi
if [ -e $file ]
then
   echo "文件存在"
else
   echo "文件不存在"
fi

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
文件可读
文件可写
文件可执行
文件为普通文件
文件不是个目录
文件不为空
文件存在

Shell内建(内置)命令

由 Bash 自身提供的命令,而不是文件系统中的某个可执行文件。

用于进入或者切换目录的 cd 命令,虽然我们一直在使用它,但如果不加以注意很难意识到它与普通命令的性质是不一样的:该命令并不是某个外部文件,只要在 Shell 中你就一定可以运行这个命令。

可以使用 type 来确定一个命令是否是内建命令:

 

user@PxeCtrlSys:~$ type cd
cd is a shell builtin  # 内建命令
user@PxeCtrlSys:~$ type ls
ls is aliased to 'ls --color=auto'
user@PxeCtrlSys:~$ type ssh
ssh is /usr/bin/ssh  # 外部命令

通常来说,内建命令会比外部命令执行得更快,执行外部命令时不但会触发磁盘 I/O,还需要 fork 出一个单独的进程来执行,执行完成后再退出。而执行内建命令相当于调用当前 Shell 进程的一个函数。

       
bash:.[aliasbgbind
breakbuiltincdcommandcompgencompletecontinue
declaredirsdisownechoenableevalexec
exitexportfcfggetoptshashhelp
historyjobskillletlocallogoutpopd
printfpushdpwdreadreadonlyreturnset
shiftshoptsourcesuspendtesttimestrap
typetypesetulimitumaskunaliasunsetwait

Shell alias:给命令创建别名

alias 用来给命令创建一个别名。

查看alias所有别名

若直接输入该命令且不带任何参数,则列出当前 Shell 环境中使用了哪些别名。

 

user@PxeCtrlSys:~$ alias
alias ls='ls --color=auto'

终于知道我的腾讯云debian上ls命令没有颜色区分了

 

# 没有为ls创建别名
root@StarMeow-Svr:~# alias
root@StarMeow-Svr:~# alias ls='ls --color=auto'
root@StarMeow-Svr:~# ls
# 这儿的文件和文件夹就有颜色区分了

使用 alias 当然也可以自定义别名,比如说一般的关机命令是shutdown-h now,写起来比较长,这时可以重新定义一个关机命令,以后就方便多了。使用 alias 定义的别名命令也是支持 Tab 键补全的,如下所示:

 

alias myShutdown='shutdown -h now'

注意,这样定义别名只能在当前 Shell 环境中有效,换句话说,重新登录后这个别名就消失了。

永久生效alias别名

为了确保永远生效,可以将该别名手动写入到用户主目录中的.bashrc文件。

 

root@StarMeow-Svr:~# vim /root/.bashrc 

# 将下方代码取消被注释
export LS_OPTIONS='--color=auto' 
eval "`dircolors`" 
alias ls='ls $LS_OPTIONS' 
alias ll='ls $LS_OPTIONS -l' 
alias l='ls $LS_OPTIONS -lA' 

# 修改完后使其生效
root@StarMeow-Svr:~# source ~/.bashrc 
root@StarMeow-Svr:~# ls
# 这儿的文件和文件夹就有颜色区分了

root@StarMeow-Svr:~# alias
alias l='ls $LS_OPTIONS -lA'
alias ll='ls $LS_OPTIONS -l'
alias ls='ls $LS_OPTIONS'

删除alias别名

使用 unalias 内建命令可以删除当前 Shell 环境中的别名。unalias 有两种使用方法:

  • 第一种用法是在命令后跟上某个命令的别名,用于删除指定的别名。
  • 第二种用法是在命令后接-a参数,删除当前 Shell 环境中所有的别名。

同样,这两种方法都是在当前 Shell 环境中生效的。要想永久删除在.bashrc文件中定义的别名,只能进入该文件手动删除。

 

# 例如已经通过alias查到如下别名ls
user@PxeCtrlSys:~$ alias
alias ls='ls --color=auto'
# 使用unalias ls就可以删除当前环境的别名
user@PxeCtrlSys:~$ unalias ls

Shell echo:输出字符串

用于字符串输出echo string

显示普通字符串

 

echo "It is a string1"

这里的双引号可以完全省略

 

echo It is a string2

显示转义字符

 

#!/bin/bash

echo "\"It is a string1\""
echo \"It is a string2\"

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
"It is a string1"
"It is a string2"

默认情况下,echo 不会解析以反斜杠\开头的转义字符。比如,\n表示换行,echo 默认会将它作为普通字符对待。

 

echo "hello \nworld"
# 运行结果
hello \nworld

echo -e "hello \nworld"
# 运行结果
hello
world

同样双引号都是可以省略的

显示变量

read命令从标准输入中读取一行,并把输入行的每个字段的值指定给shell变量

 

#!/bin/bash

read name
echo "You entered $name"

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
66
You entered 66

显示换行:-e参数和\n

 

#!/bin/bash

echo -e "this is first line \n"
echo "next"

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
this is first line 

next

输出中-e表示开启转义,\n表示换行

显示不换行:-e参数和\c或-n参数

 

#!/bin/bash

echo -e "this is first line \c"
echo "next"

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
this is first line next

-e开启转义,\c表示不换行

 

echo -n "this is first line"
echo -n "next"

# 运行结果
this is first linenext

输出结果重定向到文件

 

#!/bin/bash

echo -e "this is first line" > file.ts

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
user@PxeCtrlSys:~$ ls
file.ts software  test.sh
user@PxeCtrlSys:~$ cat file.ts 
this is first line

原样输出,不转义,不取变量

直接使用单引号即可

 

#!/bin/bash

name='lr'
echo '$name"\'

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
$name"\

显示命令执行结果

使用反引号,而不是单引号,可以执行Linux的命令

 

#!/bin/bash

echo `date`

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
Tue Mar 5 10:41:55 CST 2019

Shell exit:退出Shell命令

exit 是一个 Shell 内置命令,用来退出当前Shell:

  • 如果在终端中直接运行 exit 命令,会退出当前登录的 Shell,并关闭终端;
  • 如果在Shell中出现 exit 命令,会停止执行后边的所有代码,立即退出 Shell 脚本。

exit 命令可以接受的参数是一个状态值 n,代表退出时的状态。如果不指定,默认状态值是 0。

 

#!/bin/bash

echo "exit命令前输出"
exit 9
echo "exit命令后输出"

# 运行结果
user@PxeCtrlSys:~$ ./test.sh 
exit命令前输出  # 也就是说exit后面的语句已经不会执行了

# 紧接着用 $? 来获取test.sh的退出状态
user@PxeCtrlSys:~$ echo $?
9

Shell ulimit:显示并设置进程资源速度

系统的可用资源是有限的,如果不限制用户和进程对系统资源的使用,则很容易陷入资源耗尽的地步,而使用 ulimit 命令可以控制进程对可用资源的访问(ulimit 是一个 Shell 内置命令)。

默认情况下 Linux 系统的各个资源都做了软硬限制,其中硬限制的作用是控制软限制(换言之,软限制不能高于硬限制)。使用ulimit -a可以查看当前系统的软限制,使用命令ulimit -a –H可查看系统的硬限制。

ulimit -a查看软限制

 

user@PxeCtrlSys:~$ ulimit -a
core file size          (blocks, -c) 0
# core文件大小,单位是block,默认为0
data seg size           (kbytes, -d) unlimited
# 数据段大小,单位是kbyte,默认不做限制
scheduling priority             (-e) 0
# 调度优先级,默认为0
file size               (blocks, -f) unlimited
# 创建文件的大小,单位是block,默认不做限制
pending signals                 (-i) 15596
# 挂起的信号数量,默认是8192
max locked memory       (kbytes, -l) 64
# 最大锁定内存的值,单位是kbyte,默认是32
max memory size         (kbytes, -m) unlimited
# 最大可用的常驻内存值,单位是kbyte,默认不做限制
open files                      (-n) 65536
# 最大打开的文件数,默认是1024
pipe size            (512 bytes, -p) 8
# 管道最大缓冲区的值
POSIX message queues     (bytes, -q) 819200
# 消息队列的最大值,单位是byte
real-time priority              (-r) 0
# 程序的实时性优先级,默认为0
stack size              (kbytes, -s) 8192
# 栈大小,单位是kbyte
cpu time               (seconds, -t) unlimited
# 最大cpu占用时间,默认不做限制
max user processes              (-u) 15596
# 用户最大进程数,默认是8192
virtual memory          (kbytes, -v) unlimited
# 最大虚拟内存,单位是kbyte,默认不做限制
file locks                      (-x) unlimited
# 文件锁,默认不做限制

每一行中都包含了相应的改变该项设置的参数,以最大可以打开的文件数为例(open files 默认是 1024),想要增大至 4096 则按照如下命令设置(可参照此方法调整其他参数)。

 

# -n参数是设置最大文件打开数
# 下面命令会同时设置硬限制和软限制
user@PxeCtrlSys:~$ ulimit -n 65536

# 使用-S参数单独设置软限制
user@PxeCtrlSys:~$ ulimit -S -n 65536

# 使用-H参数单独设置硬限制
user@PxeCtrlSys:~$ ulimit -H -n 65536

limits.conf 配置文件

使用 ulimit 直接调整参数,只会在当前运行时生效,一旦系统重启,所有调整过的参数就会变回系统默认值。所以建议将所有的改动放在 ulimit 的系统配置文件中。

 

user@PxeCtrlSys:~$ 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>
#
#Where:
#<domain>取值如下:一个用户名、一个组名,组名前面用@符号、通配符*、通配符%
#<domain> can be:
#        - a 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
#        - NOTE: group and wildcard limits are not applied to root.
#          To apply a limit to the root user, <domain> must be
#          the literal username root.
#
#<type>有两个可用值:soft用于设置软限制、hard用于设置硬限制
#<type> can have the two values:
#        - "soft" for enforcing the soft limits
#        - "hard" for enforcing hard limits
#
#<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 (KB)
#        - 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 values: [-20, 19]
#        - rtprio - max realtime priority
#        - chroot - change root to directory (Debian-specific)
#
#<domain>      <type>  <item>         <value>
#
#以下是使用样例
#*               soft    core            0
#root            hard    core            100000
#*               hard    rss             10000
#@student        hard    nproc           20
#@faculty        soft    nproc           20
#@faculty        hard    nproc           50
#ftp             hard    nproc           0
#ftp             -       chroot          /ftp
#@student        -       maxlogins       4

# End of file


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值