一、关于Shell
1、Shell是什么
Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁,Shell 既是一种命令语言,又是一种程序设计语言,可以简单的理解为将各种Linux命令写入到执行文件中。
Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
2、为什么要学习Shell
Shell属于内置的脚本,程序开发的效率非常高,依赖于功能强大的命令可以迅速地完成开发任务(批处理)语法简单,代码写起来比较轻松,简单易学,项目中常通过shell脚本启动/停止程序,做一些定时任务等(如定时备份数据,定时清理日志文件)
3、Shell的第一个Hello World
#!/bin/bash
echo "Hello World !"
#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,表示使用哪一种 Shell。#!bin/sh Bourne shell版本;#!bin/bash Bourne Again Shell 版本
echo 命令用于向窗口输出文本
如果执行sh文件的时候提示'-bash: ./shell.sh: Permission denied',执行chmod +x ./sh文件名,使脚本具有执行权限
二、Shell 变量
1、变量命名规则
- 命名只能使用英文字母,数字和下划线“_”,但不能以数字开头
- 等号左右两侧不能有空格,变量的值如果有空格,需要使用单引号或双引号包括,如str='hello word'
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
# = 前后有空格,值有空格会报错
str = hello word
echo ${str}
[root@i-eikjpmcq data]# sh shell.sh
shell.sh: line 2: str: command not found
[root@i-eikjpmcq data]#
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
#=前后不要有空格,值如果有空格,用单引号或双引号包起来
str='hello word'
echo ${str}
[root@i-eikjpmcq data]# sh shell.sh
hello word
[root@i-eikjpmcq data]#
- 不能使用bash里的关键字做变量名(可用help命令查看保留关键字)
- 环境变量建议大写,便于区分
2、使用变量
在变量名前面加‘$’,如$变量名或者${变量名},符号'{}'是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,推荐给所有变量加上花括号,这是个好的编程习惯
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
str='hello'
echo $str
#{}为可选项,推荐加上
echo ${str}
[root@i-eikjpmcq data]# sh shell.sh
hello
hello
[root@i-eikjpmcq data]#
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
str='hello'
#如果不加{},解析器会把strTOM当成一个变量名,但实际上并没有这个变量,输出为空
echo "$strTOM"
[root@i-eikjpmcq data]# sh shell.sh
[root@i-eikjpmcq data]#
已定义的变量,可以被重新定义,注意,第二次赋值的时候不能写$str= " word " , 使用变量的时候才加$
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
str='hello'
echo ${str}
str='word'
echo ${str}
#再次复制的时候不要使用$
${str}='csdn'
echo ${str}
[root@i-eikjpmcq data]# sh shell.sh
hello
word
shell.sh: line 8: word=csdn: command not found
word
[root@i-eikjpmcq data]#
3、只读变量
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
str='hello'
readonly str
echo ${str}
#str使用reabonly关键字设置为只读,再次赋值会报错
str='word'
echo ${str}
[root@i-eikjpmcq data]# sh shell.sh
hello
shell.sh: line 7: str: readonly variable
[root@i-eikjpmcq data]#
4、删除变量
使用 unset 命令可以删除变量,变量被删除后不能再次使用,unset 命令不能删除只读变量
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
str1='CSDN'
unset str1
#str1已经使用unset进行删除,输出为空
echo ${str1}
str2='hello'
readonly str2
#str2使用reabonly关键字设置为只读,不能删除
unset str2
echo ${str2}
[root@i-eikjpmcq data]# sh shell.sh
shell.sh: line 12: unset: str2: cannot unset: readonly variable
hello
[root@i-eikjpmcq data]#
5、变量类型
-
用户自定义变量: 最常见的变量,由用户自由定义变量名和变量的值。
-
环境变量(/etc/profile): 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
#使用export声明的变量即是环境变量
export age="18"
#删除环境变量gender
unset gender
#env命令和set命令的区别:
#set命令可以查看所有变量,而env命令只能查看环境变量。
[root@i-eikjpmcq ~]# env
XDG_SESSION_ID=6763
HOSTNAME=i-eikjpmcq
TERM=xterm
SHELL=/bin/bash
HISTSIZE=1000
SSH_CLIENT=192.168.4.220 51138 22
SSH_TTY=/dev/pts/1
USER=root
[root@i-eikjpmcq ~]#
-
位置参数变量: 这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的。
位置参数变量 | 作用 |
---|---|
$n | n为数字,$0表示当前Shell脚本程序的名称,$1-9代表第一到第九个参数,十以上的参数需要用大括号包含,如{10} |
$* | 这个变量代表命令行中所有的参数,$把所有的参数看成一个整体 |
$@ | 这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待 |
$# | 这个变量代表命令行中所有参数的个数 |
-
预定义变量: 是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的
预定义变量 | 作用 |
---|---|
$? | 最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非О(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。 |
$$ | 当前进程的进程号(PID) |
$! | 后台运行的最后一个进程的进程号(PID) |
三、Shell字符串
字符串是shell编程中最常用最有用的数据类型,字符串可以用单引号,也可以用双引号,也可以不用引号,单引号和双引号一般是为了解决字符串值中的空格
1、单引号
- 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
str1='我是单引号中的值 哈哈'
echo ${str1}
#单引号包含的读取变量无效,会原样输出,但是双引号会正确输出变量值
echo '${str1}'
[root@i-eikjpmcq data]# sh shell.sh
我是单引号中的值 哈哈
${str1}
[root@i-eikjpmcq data]#
- 单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
#单引号不能单独(不成对)出现,即使使用\转义也不行
str1='我是\'单引号中的值 哈哈'
echo ${str1}
[root@i-eikjpmcq data]# sh shell.sh
shell.sh: line 2: unexpected EOF while looking for matching `''
shell.sh: line 4: syntax error: unexpected end of file
[root@i-eikjpmcq data]#
#单引号拼接变量
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
str1='马云'
echo 'hello,'${str1}'!'
[root@i-eikjpmcq data]# sh shell.sh
hello,马云!
[root@i-eikjpmcq data]#
2、双引号
- 双引号里可以有变量,可以拼接字符串
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
str1="我是双引号引号中的值 哈哈"
echo "hello word ${str1}"
[root@i-eikjpmcq data]# sh shell.sh
hello word 我是双引号引号中的值 哈哈
[root@i-eikjpmcq data]#
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
str1='马云'
#双引号拼接变量
echo "hello,"${str1}"!"
echo "hello,${str1}!"
[root@i-eikjpmcq data]# sh shell.sh
hello,马云!
hello,马云!
[root@i-eikjpmcq data]#
- 双引号里可以出现转义字符
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
str1="我是\"双引号中的值 哈哈"
echo ${str1}
[root@i-eikjpmcq data]# sh shell.sh
我是"双引号中的值 哈哈
[root@i-eikjpmcq data]#
3、获取字符串长度
${#变量名}
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
str1="阿里巴巴是马云的"
echo "str1的变量值长度是:${#str1}"
[root@i-eikjpmcq data]# sh shell.sh
str1的变量值长度是:8
[root@i-eikjpmcq data]#
4、提取子字符串
${变量名:startIndex:endIndex}
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
str1="阿里巴巴是马云的"
result=${str1:0:4}
#注意:第一个字符的索引值为 0
echo "获取str1变量值的0-4个字符:${result}"
[root@i-eikjpmcq data]# sh shell.sh
获取str1变量值的0-4个字符:阿里巴巴
[root@i-eikjpmcq data]#
四、Shell数组
bash支持一维数组(不支持多维数组),并且没有限定数组的大小,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0
1、定义数组
数组名=(value1 value2 ... valuen)
第一种:
names=(张三 李四 王五 二狗)
第二种:
names=(
张三
李四
王五
二狗
)
第三种:可以不使用连续的下标,而且下标的范围没有限制
names[0]=张三
names[1]=李四
names[6]=王五
......
names[n]=valuen
2、读取数组
${数组名[index]} 注:index为@ 表示获取数组的所有值
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
names=(张三 李四 王五 二狗)
echo "获取数组names下标为2的值:${names[2]}"
[root@i-eikjpmcq data]# sh shell.sh
获取数组names下标为2的值:王五
[root@i-eikjpmcq data]#
#${数组名[@]}获取数组的所有元素
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
names=(张三 李四 王五 二狗)
echo "获取数组names的所有值:${names[@]}"
[root@i-eikjpmcq data]# sh shell.sh
获取数组names的所有值:张三 李四 王五 二狗
[root@i-eikjpmcq data]#
3、获取数组的长度
${#数组名[@]}、${#数组名[*]}、${#数组名[index]}
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
names=(张三 李四 王五 二狗)
#使用${#数组名[@]}获取数组长度
echo "使用@获取数组的长度:${#names[@]}"
#使用${数组名[*]}获取数组的长度
echo "使用*获取数组长度:${#names[*]}"
#使用${#数组名[n]}获取单个元素的长度
echo "使用下标获取单个元素的长度:${#names[0]}"
[root@i-eikjpmcq data]# sh shell.sh
使用@获取数组的长度:4
使用*获取数组长度:4
使用下标获取单个元素的长度:2
[root@i-eikjpmcq data]#
五、Shell中的运算符和特殊符号
一、运算符
-
算数运算符
Shell 和其它编程语言不同,Shell 不能直接进行算数运算,必须使用数学计算命令,如下:
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
sum=6+5
echo ${sum}
[root@i-eikjpmcq data]# sh shell.sh
6+5
[root@i-eikjpmcq data]#
可以看出并不是打印的11而是打印的6+5,Shell 不会直接进行算术运算,而是把+两边的数据(数值或者变量)当做字符串,把+当做字符串连接符,最终的结果是把两个字符串拼接在一起形成一个新的字符串。
这是因为,在 Bash Shell 中,如果不特别指明,每一个变量的值都是字符串,无论你给变量赋值时有没有使用引号,值都会以字符串的形式存储。
换句话说,Bash shell 在默认情况下不会区分变量类型,即使你将整数和小数赋值给变量,它们也会被视为字符串,这一点和大部分的编程语言不同。
因此shell的算术运算中有以下几种方法
名称 | 语法 | 范例 | 备注 |
---|---|---|---|
算术扩展 | 变量=$((算术表达式)) | r=$((1+2*3)) | (())只支持整数的运算,小数运算会报错 |
使用外部程序expr | 变量=`expr 算术表达式` | r=`expr 1 + 2` | 用expr表示后面的表达式为一个数学运算。需要注意的是,`并不是一个单引号,而是反引号(“Tab”键上面的那个符号),表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2。r=`expr 4 * 5` #错误,乘法 r=`expr 4 \* 5` |
使用[ ] | 变量=$[算术表达式] | r=$[1+2] | |
使用内置命令declare | declare -i 变量=算术表达式 | declare -i r=1+2*3 | |
使用内置命令let | let 算术表达式 | let r=1+2 | 用let表示后面的表达式为一个数学运算。表达式中的变量前不必有$.如果表达式中包含了空格或其他特殊字符,则必须引起来。 例:let “I = I + 1” 或 let i=i+1 用let表示后面的表达式为一个数学运算。表达式中的变量前不必有$.如果表达式中包含了空格或其他特殊字符,则必须引起来。例:let “I = I + 1” 或 let i=i+1 |
运算符 | 说明 | 举例 |
---|---|---|
+ | 加法 | $((num1+num2)) |
- | 减法 | $((num1-num2)) |
* | 乘法 | `expr $num1 \* $num2` |
/ | 除法 | $((num1%num2)) |
% | 取余 | $((num1/num2)) |
= | 赋值 | num1=${num3} 将把变量 num3 的值赋给 num1。 |
== | 相等 | [ $num1 == $num1 ] 用于比较两个字符串,相同则返回 true,不相同返回false |
!= | 不相等 | [ $num1 != $num2 ] 用于比较两个字符串,不相同则返回 true,相同返回false |
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
num1=20
num2=10
# +
echo "num1+num2:$((num1+num2))"
# -
echo "num1-num2:$((num1-num2))"
# * 乘法符号需要转义
echo 'num1*num2=' `expr $num1 \* $num2`
# %
echo "num1%num2:$((num1%num2))"
# /
echo "num1/num2:$((num1/num2))"
#赋值 =
num3=40
num1=${num3}
echo "将num3的值${num3}赋值给num1,num1赋值后的值为:${num1}"
# == 判断相等
str1="a"
str2="a"
if [ $str1 == $str2 ]
then
echo 'str1等于str2'
else
echo 'str1不等于str2'
fi
# != 判断不等
if [ $a != $b ]
then
echo 'a不等于b'
else
echo 'a等于b'
fi
[root@i-eikjpmcq data]# sh shell.sh
num1+num2:30
num1-num2:10
num1*num2= 200
num1%num2:0
num1/num2:2
将num3的值40赋值给num1,num1赋值后的值为:40
str1等于str2
a不等于b
[root@i-eikjpmcq data]#
-
关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字
关系 | 运算符 | 示例 |
---|---|---|
等于 | -eq:用于整数的比较 ==:用于字符串、整数的比较 | [ $num1 -eq $num1 ]、[ $str1 == $str1 ] ,相同则返回 true,不相同返回false |
不等于 | -ne:用于整数的比较 !=:用于字符串、整数的比较 | [ $num1 -ne $num1 ]、[ $num1 != $num1 ],不相同则返回 true,相同返回false |
大于 | -gt | [ $num1 -gt $num2 ]左边的数是否大于右边的数,如果是返回 true。不是返回false |
小于 | -lt | [ $num1 -gt $num2 ]左边的数是否小于右边的数,如果是返回 true。不是返回false |
大于等于 | -ge | [ $num1 -ge $num2 ]左边的数是否大于等于右边的数,如果是返回 true。不是返回false |
小于等于 | -le | [ $num1 -le $num2 ]左边的数是否小于等于右边的数,如果是返回 true。不是返回false |
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
# eq比较数字
num1=20
num2=18
if [ $num1 -eq $num2 ]
then
echo '是否-eq:true'
else
echo '是否-eq:false'
fi
# == 比较字符串、数字
str1=a
str2=a
if [ $str1 == $str2 ]
then
echo '是否==:true'
else
echo '是否==:false'
fi
# -gt 大于
if [ $num1 -gt $num2 ]
then
echo '是否-gt:true'
else
echo '是否-gt:false'
fi
# -lt 小于
if [ $num1 -lt $num2 ]
then
echo '是否-lt:true'
else
echo '是否-lt:false'
fi
# -ge 大于等于
num1=50
num2=30
if [ $num1 -ge $num2 ]
then
echo '是否-ge:true'
else
echo '是否-ge:false'
fi
# -le 小于等于
num1=50
num2=30
if [ $num2 -le $num1 ]
then
echo '是否-le:true'
else
echo '是否-le:false'
fi
[root@i-eikjpmcq data]# sh shell.sh
是否-eq:false
是否==:true
是否-gt:true
是否-lt:false
是否-ge:true
是否-le:true
[root@i-eikjpmcq data]#
-
逻辑运算符
运算符 | 说明 | 举例 |
---|---|---|
! | 非运算,表达式为 true 则返回 false,否则返回 true。 | [ ! false ] 返回 true。 |
-o | 或(或者)运算,有一个表达式为 true 则返回 true。 | [ $a -lt 20 -o $b -gt 100 ] 返回 true。一个为真即为真,全部为假才是假 |
-a | 与(并且)运算,两个表达式都为 true 才返回 true。 | [ $a -lt 20 -a $b -gt 100 ] 返回 false。一个为假即为假,全部为真才是真 |
[root@i-eikjpmcq data]# cat shell.sh
#!/bin/bash
#! 取反
num1=20
num2=20
#num1和num2相等,加了!,表示结果取反,所以输出为false
if [ !$num1 -eq $num2 ]
then
echo '是否等于:true'
else
echo '是否等于:false'
fi
# -o 或者
num1=10
num2=30
if [ $num1 -gt 20 -o $num2 -gt 40 ]
then
echo 'num1大于20或者num2大于40满足其中一个条件'
else
echo 'num1大于20或者num2大于40两个条件都不满足'
fi
# -a 并且
num1=10
num2=30
if [ $num1 -gt 6 -a $num2 -gt 20 ]
then
echo 'num1大于6并且num2大于20这两个条件都满足'
else
echo 'num1大于6并且num2大于20两个条件没有同时满足'
fi
[root@i-prb7e83w data]# sh shell.sh
是否等于:false
num1大于20或者num2大于40两个条件都不满足
num1大于6并且num2大于20这两个条件都满足
-
字符串运算符
运算符 | 说明 | 举例 |
---|---|---|
-z | 检测字符串长度是否为0,为0返回 true。 | [ -z $strName ] 返回 false。 |
-n | 检测字符串长度是否为0,不为0返回 true。 | [ -n $strName ] 返回 true。 |
strName | 检测字符串是否为空,不为空返回 true。 | [ $strName ] 返回 true。 |
[root@i-prb7e83w data]# cat shell.sh
#!/bin/bash
str1=''
if [ -z $str1 ]
then
echo '长度是否为0:true'
else
echo '长度是否为0:false'
fi
str2='hello'
if [ -z $str2 ]
then
echo '长度是否不为0:true'
else
echo '长度是否不为0:false'
fi
if [ $str2 ]
then
echo '是否不为空:true'
else
echo '是否为空:false'
fi
[root@i-prb7e83w data]# sh shell.sh
长度是否为0:true
长度是否不为0:false
是否不为空:true
[root@i-prb7e83w data]#
-
文件测试运算符
如果同时存在几个文件权限的判断,只要有一个部分符合,则认为是有权限的。(下图红色加粗的几个为常用命令)
操作符 | 说明 | 举例 |
---|---|---|
-b file | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] 返回 false。 |
-c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | [ -b $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。 |
[root@i-prb7e83w data]# cat shell.sh
#!/bin/bash
file=/data/shell.sh
# -d 检测是否是目录
if [ -d $file ]
then
echo "-d:$file是目录"
else
echo "-d:$file不是目录"
fi
# -f 检测是否是普通文件
if [ -f $file ]
then
echo "-f:$file是普通文件"
else
echo "-f:$file不是普通文件"
fi
# -r 检测是否可读
if [ -r $file ]
then
echo "-r:$file可读"
else
echo "-r:$file不可读"
fi
# -w 检测是否可写
if [ -w $file ]
then
echo "-w:$file可写"
else
echo "-w:$file不可写"
fi
# -x 检测是否可执行
# 如果结果为不可执行,使用命令 chmod +x 文件名,文件就变为可执行了
if [ -x $file ]
then
echo "-x:$file可执行"
else
echo "-x:$file不可执行"
fi
# -s 检测文件是否为空
if [ -d $file ]
then
echo "-d:$file为空"
else
echo "-d:$file不为空"
fi
# -e 检测文件、文件夹是否存在
if [ -e $file ]
then
echo "-e:$file存在"
else
echo "-e:$file不存在"
fi
[root@i-prb7e83w data]# sh shell.sh
-d:/data/shell.sh不是目录
-f:/data/shell.sh是普通文件
-r:/data/shell.sh可读
-w:/data/shell.sh可写
-x:/data/shell.sh可执行
-d:/data/shell.sh不为空
-e:/data/shell.sh存在
二、特殊运算符
符号 | 作用 |
---|---|
’ ’ | 单引号。在单引号中所有的特殊符号,如“$”和”(反引号)都没有特殊含义。单引号括起来的都是普通字符,会原样输出 |
“ ” | 双引号。在双引号中特殊符号都没有特殊含义,但是“$”,"`"(esc键下面)和“\”是例外,拥有“调用变量的值”、“引用命令”和“转义符”的特殊含义。 |
· · | 反引号。反引号括起来的内容是系统命令,在Bash中会先执行它。和 ()作用一样,不过推荐使用 (),因为反引号非常容易看错。 |
$() | 和反引号作用一样,用来引用系统命令。(推荐使用) |
() | 用于一串命令执行时,()中的命令会在子Shell中运行 |
{} | 用于一串命令执行时,{ }中的命令会在当前Shell中执行。也可以用于变量变形与替换。 |
[ ] | 用于变量的测试。 |
# | 1.在shell文件的行首,作为声明标记,#!/bin/bash; 2. 其他地方作为注释使用,在一行中,#后面的内容并不会被执行 3. 但是用单/双引号包围时,#作为#号字符本身,不具有注释作用。 |
$ | 用于调用变量的值,如需要调用变量name的值时,需要用$name的方式得到变量的值。 |
\ | 转义符,跟在\之后的特殊符号将失去特殊含义,变为普通字符。如$将输出“$”符号,而不当做是变量引用。 |