什么是Shell
有人说Linux系统犹如核桃,有外壳和内核;
内核:kernal,Linus集中精力研究内核开发;
外壳:shell,是有命令行或者界面的应用程序;
狭义的shell指一种应用程序,提供了一个界面,用户通过这个应用才可以访问内核服务,广义的shell指一切应用程序,一般来说,linux学习应该先学shell,再学内核
shell脚本:shell script,是用shell编写的脚本程序,简称shell;
在学习windows时,会接触bat脚本,用于批处理,linux则需要学习shell
shell的意义
windows的理念是大而全,比如Visual Studio是个很好用的集成开发环境,但它实在体型巨大,这种大而全的理念适用于商业风格,不易做嵌入;
Linux是具备开源精神的,由于开源特性,它的程序也需要做到能适合不同需求的使用者,即要求应用程序便于组装,而shell脚本就是一个粘合剂,通过shell,可以将多个程序(命令)组装为一个大型项目
所谓shell,一般就是将命令组合,放到一个脚本内执行,shell也算是一种编程语言;
shell的开发流程:需求分析->建模->伪代码逻辑->代码实现;
当服务器上没有GUI时,可以通过sftp在本地写代码,同步到服务器执行,对于有GUI的服务器,可以直接在服务器上用visual studio code开发
Shell编程规则
Shell编程最好遵守一定的规范,变量和函数名小写,常量大写,函数内的变量加local
Shell编程
创建脚本
在命令行输入code打开vscode:
code
在vscode内创建hello.sh:
第一行为:
#!/bin/bash
这是向linux指明了,该文件要用bash解析,补充一下,在/etc/shells可以看到系统有哪些shell:
也可以查看默认的shell:
echo $SHELL
现在,我在hello.sh内键入echo "hello world\n"
,echo类似于标准输出,现在执行脚本不会对"\n"转义,要自动换行需要在echo后加参数-e,开启转义功能:
echo -e "hello world"
保存后,回到命令行,执行该文件,如果权限没有x(可执行),需要chmod改变权限,执行过程为:
注意,如果是在Windows下写的文件,到linux运行会遇到编码的差异,所以最好使用dos2unix达到编码转换的作用:
dos2unix ./filename.sh
注释
首先是单行注释:#
,当然,行首用于解释,选择shell类型;
多行注释为:
:<<EOF
被注释的内容
EOF
Shell变量
shell变量命名只用英文,数字,下划线,同时不能使用shell的关键字;
变量分三种:局部变量,环境变量,特殊变量
局部变量
局部变量在当前shell中有效,在脚本或命令中定义,函数内的局部变量要加上local;
变量可以定义,使用,只读,删除:
a=baijingyi
b="baijingyi"
a和b的定义实质是一样的,加双引号""有推荐的意思,规范是最好加上双引号;
然后用echo输出:
echo "$a"
或者
echo $a
或者
echo ${a}
echo加不加双引号也没关系,在bash中,$开头表示变量,另外{…}用于变量替换:
echo ${var}abc
如果var值为1,则输出1abc
;
另外我可以定义只读变量:
#!/bin/bash
readonly name
name="baijingyi"
echo "${name}"
执行脚本不会输出结果baijingyi,因为name是只读的,不允许赋值;
也可以删除变量:
unset name
环境变量
全局变量,所有程序都可以访问,建议大写;
env 查看当前的环境变量
export 导出环境变量
source 引入环境变量
现在脚本中新建变量,用export可以导出到环境变量中,但脚本结束后,该变量将被释放,所以需要用source使变量在当前会话中生效:
#!/bin/bash
#导出环境变量
export MY_NAME="baijingyi"
#查看
env | grep MY_NAME
假设文件名为hello.sh,执行使用:
source ./hello.sh
特殊变量
常见特殊变量如下:
echo $0 当前脚本文件名
echo $@ 输入脚本的所有参数
echo "$#" 参数的个数
echo "$$" 当前shell的进程ID
基本运算
算术运算
加减乘除和取余:
#!/bin/bash
a=11
b=5
#运算, 使用关键字expr,注意不是单引号,是反斜撇
val=`expr $a + $b`
echo "$a+$b=$val"
#另一种运算写法
val=$[a-b]
echo "$a-$b=$val"
关系运算
-eq 相等
-ne 不相等
-gt 大于
-ge 大于等于
-lt 小于
-le 小于等于
比如:
#!/bin/bash
#关系运算
a=11
b=5
#例子,注意中括号需要空格
if [ $a -eq $b ]
then
echo "$a -eq $b : a等于b"
else
echo "$a -eq $b :a不等于b"
#fi为if语句的结束
fi
布尔与逻辑运算
! 非运算
&& 逻辑与
|| 逻辑或
== 相等
!= 不相等
比如:
#!/bin/bash
#布尔运算
a=11;b=5
#注意中括号需要空格
if [ $a -eq $b ]
then
echo "a等于b"
else
echo "a不等于b"
fi
#逻辑运算 注意有两个中括号
#注意中括号需要空格
if [[ $a -gt 0 && $b -gt 0 ]]
then
echo "a,b大于0"
fi
文件测试运算
-d 是否为目录
-f 是否为普通文件
-r,-w,-x 是否可读可写可执行
-s 文件是否为空
-e 文件是否存在
实例:
#!/bin/bash
file=$0 #$0指当前脚本文件名
echo "文件是:$file"
#注意中括号需要空格
if [ -f $file ]
then
echo "是普通文件"
fi
字符串,数组,分支循环,函数
字符串
字符串分两种,单引号和双引号;
单引号的字符串会原样输出,变量无效;双引号的字符串可以识别变量:
#!/bin/bash
course="Linux"
#单引号
seqence='学习$course'
echo seqence
#双引号
seqenceed="学习$course"
echo seqenceed
因此,如果要在字符串中引用变量,最好使用双引号;
字符串的三个常用技巧:
#!/bin/bash
#字符串长度
str="helloworld"
echo "字符串"$str"的长度为"${#str}
#获取子串,从第1个字符开始,取3个,即ell
echo "字符串"$str"的子串"${str:1:3}
#查找子串
matched=`expr index "$str" wo`
echo "字符串"$str"的wo子串位置为"$matched
#应该输出5
数组
数组下标从0开始,在shell内可以定义数组,修改数组,读数组,获取长度:
#!/bin/bash
#数组定义
arr=(aa bb cc "hello")
#修改元素
arr[2]="222"
#读数组
echo "读arr[2]"${arr[2]}
echo "所有元素"${arr[@]}
#获取数组长度
len=${#arr[@]}
echo "数组长度为"$len
分支
分支可以用if,elif,else,但这样的方式是顺序执行的分支,也可以用并行执行的开关case;
使用if else的分支:
#!/bin/bash
age=20
if [ $age -le 10 ]
then
echo "少年"
elif [ $age -le 20 ]
then
echo "青年"
else
echo "其他"
#if的结束标志
fi
使用case的分支:
#!/bin/bash
status=1
case $status in
0) echo "todo"
;;
1) echo "doing"
;;
2) echo "done"
;;
esca
循环
循环一般常用for循环for...in...do...done
:
#!/bin/bash
arr=(aa bb cc)
for item in ${arr[@]}
do
echo "$item"
done
循环遇到break时,代表跳出当前的循环;
函数
shell脚本支持函数定义和调用,为了提高程序复用效率:
#!/bin/bash
#函数定义
function myfunc()
{
echo "这是一个shell函数"
}
#与python不同,shell的函数调用使用函数名
myfunc
函数的参数传递和返回值:
#!/bin/bash
function madd()
{
local ret=$[$1+$2]
return $ret
}
madd 5 8
# $?是一个特殊变量:上一个指令的返回值
echo $?
应用实例
猜数字
通过shell实现一个任务:
1.随机从0到99生成一个数字;
2.使用者手动输入数字;
3.程序判断,如果数字不同,告知是大了还是小了,使用者根据提示继续输入,直到数字正确,返回猜数字的次数,退出程序;
#!/bin/bash
#产生一个两位的随机数
answer=$(date +%s%N | cut -c16-17)
count=0
while :
do
#记录判断次数
count=$[count+1]
echo -n "输入0到99之间的数字"
#获取使用者的输入
read aNum
#如果相同,则退出
if [ $aNum -eq $answer ]
then
echo "正确,一共猜了${count}次"
break;
#再比较,给出提示信息
elif [ $aNum -lt $answer ]
then
echo "小于正确值"
else
echo "大于正确值"
fi
done
获取CPU使用情况
#!/bin/bash
cat /proc/stat | grep "cpu"
探测本地网络
对一个局域网内的ip地址,探测是否畅通:
#!/bin/bash
function testping()
{
local ip=$1
#判断ip字符串的长度是否为0
if [ -z "$ip" ]
then
echo "参数为空"
return
fi
#ping的结果不要保存,放到/dev/null里相当于丢弃
ping -c1 $ip &>/dev/null
#ping的返回值为0代表正常
if [ $? -eq 0 ]
then
echo "$ip on"
else
echo "$ip off"
fi
}
for ((i=1; i<255; i++))
do
# &符号比较特殊,代表函数调用是并发的
testping "192.168.31.$i" &
done
#并发时需要wait
wait
echo "任务结束"