shell介绍
shell命令:
在linux终端能被解析的命令,就是shell命令。
shell脚本:
多个shell命令的集合,可以加上一些逻辑判断、循环、函数、变量等内容,写在一个文件中,这个文件是一个可执行的程序文件,当执行这个文件的时候,文件中的所有shell命令都可以被执行,这个文件就叫做shell脚本。
shell命令的解析过程:
-
检测输入
在终端输入命令的时候,解析器会检测输入,检测到回车表示命令输入完毕,例:当我们输入pwd 回车的时候,解析器接收检测到输入了回车,表示这个命令输入完毕了,再开始解析。
-
解析命令
有一个解析命令的进程,是bash或者shell。解析器首先检查环境变量PATH,遍历PATH中的所有路径,在各个路径中检测是否有用户输入的命令,如果找到了,执行该路径下的命令,如果没找到,继续去下一个路径中找,最终也找不到,便提示这个命令无法解析。
shell脚本的基本格式:
-
命名格式
通常,shell脚本的后缀是sh,但这个后缀并不是必须的,只是为了方便识别shell脚本。
-
书写格式
通常,第一行的内容是固定的,就是指定解析脚本所使用的命令解析器,代码如下:
#! /bin/bash 或 #! /bin/sh
其实,第一行也是可以省略的。
往下,就是写逻辑或者shell命令。
shell脚本的执行:
shell要执行必须要有执行权限,但是普通文件的权限是664(普通目录是775),也就是3种用户都没有可执行的权限,所以脚本要执行,必须要加上可执行的权限,如下:
chmod a+x 脚本文件路径 # a+x表示所有用户都加上可执行的权限
或
chmod 777 脚本文件路径
执行的命令如下:
./xxx.sh
或
sh xxx.sh
shell脚本内容格式
shell脚本中的变量:
-
定义变量
-
普通变量
语法: 变量名=变量值 # 中间不能有空格,变量名大小写都可以 例: hello=123
-
-
环境变量
语法: export 变量名=变量值 或: set 变量名=变量值 例: export PATH=$PATH:$GOROOT/bin set PATH=$PATH:$GOROOT/bin
-
位置变量
位置变量用来给取给脚本传递的参数的值
语法: # 脚本名称:test.sh # 执行命令:./test.sh 123 aaa # 脚本中取值:$0代表脚本名称,$1代表参数1,$2代表参数2 # $0的值为test.sh,$1的值为123,$2的值为aaa
例:
新建test.sh脚本文件,写入如下内容:
#! /bin/bash echo "脚本名称为:"$0 echo "第一个参数为:"$1 echo "第二个参数为:"$2
给脚本添加可执行的权限,并执行:
itcast@itcast:~/mytest$ chmod 777 test.sh itcast@itcast:~/mytest$ ./test.sh 123 aaa 脚本名称为:./test.sh 第一个参数为:123 第二个参数为:aaa itcast@itcast:~/mytest$
-
特殊变量
$#:给shell脚本传递的参数的个数 $@:给脚本传递的所有参数的集合 $$:进程的id $?:代表执行完成之后的状态(需要在执行脚本的命令之后执行) 0:代表执行成功 非0:代表执行失败
例:
新建shell脚本test.sh,写入如下内容:
#! /bin/bash echo "参数个数为:"$# echo "所有参数的集合:"$@ echo "进程id为:"$$
给脚本添加可执行的权限,并执行:
itcast@itcast:~/mytest$ chmod 777 test.sh itcast@itcast:~/mytest$ ./test.sh 123 aaa 参数个数为:2 所有参数的集合:123 aaa 进程id为:96940 itcast@itcast:~/mytest$ $? # 这个命令必须紧跟在执行的后面 0:未找到命令 itcast@itcast:~/mytest$ $? # 这个命令没有跟在执行的后面所以是非0 127:未找到命令 itcast@itcast:~/mytest$
-
变量取值
-
普通变量取值
num=123 # 定义普通变量 $num # 取值 或: ${num}
<font color="red">注意:双引号可以解析变量,单引号不能解析变量</font>
例:
itcast@itcast:~/mytest$ name=zhangsan itcast@itcast:~/mytest$ echo $name zhangsan itcast@itcast:~/mytest$ echo "$name" zhangsan itcast@itcast:~/mytest$ echo '$name' $name itcast@itcast:~/mytest$
-
-
取命令运行的结果值
str=$(shell命令) 或: str=`shell命令` 例: docker rm $(docker ps -aq) -f docker rm `docker ps -aq` -f
shell脚本的判断:
语法:
# 单分支
# 写法1
if [ 条件判断 ];then
处理命令
fi
# 写法2
if [ 条件判断 ]
then
处理命令
fi
# 双分支
if [ 条件判断 ];then
处理指令
else
处理指令
fi
# 多分支
if [ 条件判断 ];then
处理指令
elif [ 条件判断 ];then
处理指令
else
处理指令
fi
例:
在mytest目录下新建test.sh,写入如下内容
#! /bin/bash
# 将参数传入为文件名并赋值给变量
filename=$1
# 判断是一个目录时,输出是一个目录
if [ -d $filename ];then
echo "$filename是一个目录"
# 存在并且大小大于0
elif [ -s $filename ];then
echo "$filename是存在的"
else
echo "$filename不存在,或者文件为空"
fi
执行的结果:
itcast@itcast:~/mytest$ chmod 777 test.sh
itcast@itcast:~/mytest$ ls
test.sh
itcast@itcast:~/mytest$ ./test.sh aaa
aaa不存在,或者文件为空
itcast@itcast:~/mytest$ ./test.sh test.sh
test.sh是存在的
itcast@itcast:~/mytest$ ./test.sh ../mytest
../mytest是一个目录
itcast@itcast:~/mytest$
shell脚本中的循环:
语法:
for 变量名 in 列表;do
执行命令
done
例1:
脚本内容:
#! /bin/bash
# 接收参数并赋值给变量
list=$@
for v in $list;do
echo $v
done
运行及执行结果:
itcast@itcast:~/mytest$ ./test.sh 11 22 33 44
11
22
33
44
itcast@itcast:~/mytest$
例2:遍历目录
脚本内容:
#! /bin/bash
# 接收参数并赋值给变量
list=$(ls /home/itcast/go)
for v in $list;do
echo $v
done
运行结果:
itcast@itcast:~/mytest$ ./test.sh
api
AUTHORS
VERSION
itcast@itcast:~/mytest$
例3:遍历自定义列表
脚本内容:
#! /bin/bash
for v in aa bb cc dd 11 22 33;do
echo $v
done
运行结果:
itcast@itcast:~/mytest$ ./test.sh
aa
bb
cc
dd
11
22
33
itcast@itcast:~/mytest$
shell脚本中的函数:
语法:
# 定义函数
函数名(){
函数体
# 在函数体中获取参数使用位置变量,即:$@,$#,$1,$2
}
# 调用函数
# 不传参数的调用
函数名
# 带参数的调用
函数名 参数1 参数2 参数3 ...
例:
创建shell脚本并写入内容:
#! /bin/bash
# 定义函数
fun(){
# 遍历传进来的参数
for v in $@;do
# 判断存在且是目录
if [ -d $v ];then
# 存在就输出
echo "$v是一个目录"
else
# 不存在就创建目录
mkdir $v
# 判断是否创建成功
if [ $? -ne 0 ];then
echo "$v目录创建失败"
else
echo "$v创建成功"
fi
fi
done
}
# 调用函数并传入参数,参数为外部传进来的参数
fun $@
执行结果:
itcast@itcast:~/mytest$ mkdir a
itcast@itcast:~/mytest$ ls
a test.sh
itcast@itcast:~/mytest$ chmod 777 test.sh
itcast@itcast:~/mytest$ ./test.sh a b c
a是一个目录
b创建成功
c创建成功
itcast@itcast:~/mytest$
shell条件测试参考
-
文件状态测试
-b filename 当filename 存在并且是块文件时返回真(返回0) -c filename 当filename 存在并且是字符文件时返回真 <font color="red">-d pathname</font> 当pathname 存在并且是一个目录时返回真 -e pathname 当由pathname 指定的文件或目录存在时返回真 -f filename 当filename 存在并且是正规(普通)文件时返回真 -g pathname 当由pathname 指定的文件或目录存在并且设置了SGID 位时返回真 -h/-L filename 当filename 存在并且是符号链接文件时返回真 (或 filename) -k pathname 当由pathname 指定的文件或目录存在并且设置了"粘滞"位时返回真 -p filename 当filename 存在并且是命名管道时返回真 -r pathname 当由pathname 指定的文件或目录存在并且可读时返回真 <font color="red">-s filename</font> 当filename 存在并且文件大小大于0 时返回真 -S filename 当filename 存在并且是socket 时返回真 -t fd 当fd 是与终端设备相关联的文件描述符时返回真 -u pathname 当由pathname 指定的文件或目录存在并且设置了SUID 位时返回真 <font color="red">-w pathname</font> 当由pathname 指定的文件或目录存在并且可写时返回真 <font color="red">-x pathname</font> 当由pathname 指定的文件或目录存在并且可执行时返回真 -O pathname 当由pathname 存在并且被当前进程的有效用户id 的用户拥有时返回真(字母O 大写) -G pathname 当由pathname 存在并且属于当前进程的有效用户id 的用户的用户组时返回真 file1 -nt file2 file1 比file2 新时返回真 file1 -ot file2 file1 比file2 旧时返回真 f1 -ef f2 files f1 and f2 are hard links to the same file -
常见字符串测试
-z string 字符串string 为空串(长度为0)时返回真 -n string 字符串string 为非空串时返回真 str1 = str2 字符串str1 和字符串str2 相等时返回真 str1 == str2 同 = str1 != str2 字符串str1 和字符串str2 不相等时返回真 str1 < str2 按字典顺序排序,字符串str1 在字符串str2 之前 str1 > str2 按字典顺序排序,字符串str1 在字符串str2 之后 -
常见数值测试
nt1 -eq int2 如果int1 等于int2,则返回真 int1 -ne int2 如果int1 不等于int2,则返回真 int1 -lt int2 如果int1 小于int2,则返回真 int1 -le int2 如果int1 小于等于int2,则返回真 int1 -gt int2 如果int1 大于int2,则返回真 int1 -ge int2 如果int1 大于等于int2,则返回真 -
测试时使用的逻辑操作符
-a 逻辑与,操作符两边均为真,结果为真,否则为假。&& -o 逻辑或,操作符两边一边为真,结果为真,否则为假。|| ! 逻辑否,条件为假,结果为真。