前言:我们为什么要学shell编程,因为shell编程占据了Linux的半壁江山,是运维人员必会的技能之一。
Shell编程语言必知必会
Shell命令解释器:bash
目前应用最广泛一款命令解释器,红帽系列(默认),Debian,Ubuntu
编程环境准备
修改vimrc文件,达到控制vim创建,编辑文件的动作,当前用户家目录下 /etc/vimrc
这有助于我们创建以.sh文件的时候自动添加内容。
set ignorecase
autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()"
func SetTitle()
if expand("%:e")=='sh'
call setline(1, "#!/bin/bash")
call setline(2,"##############################################################")
call setline(3, "# File Name:".expand("%"))
call setline(4, "# Version:V1.0")
call setline(5, "# Author:xzb996")
call setline(6, "# Organization:www.oldboyedu.com")
call setline(7, "# Desc:")
call setline(8,"##############################################################")
endif
endfunc
在创建一个工作目录吧
Shell脚本执行方式
通过sh或bash
#!符号含义
并非注释的意思,写在脚本开头,用于指定脚本默认的命令解释器。
OK,上述的环境我们都准备好了之后,我们开始shell编程之路吧
1. 从变量开始。
查看变量内容$变量名字,赋值oldboy=xzb996
#创建变量
oldboy=xzb996
#查看变量用$
echo $oldboy
1.1 变量命名
1.不能以数字开头。
2. 不能以特殊符号开头,可以以"_"开头。
3. 推荐字母开头
4.驼峰方式: personOfName personOfAge
1.2 变量分类
分类 说明 普通变量 (局部变量) 我们在脚本中通过oldboy=xzb创建出来的变量 环境变量(全局变量) 一般都是系统创建,PATH 特殊变量 shell脚本,命令,各种方面
局部变量
#创建变量
day=6
#用$取变量
echo $day
#用${}取变量
echo ${day}天
$与${}一致,可以用于取值。${变量} ,指定变量名字
环境变量
全局变量:1处定义,处处使用
大部分都是系统定义的,我们一般就是修改
env
export
declare
环境变量名字 含义
PATH 记录命令位置的环境变量,运行命令的时候bash会在PATH的 路径中查找 LANG 系统语言与字符集
#export 创建或修改全局变量(环境变量),把当前系统语言改为中文,字符集改为UTF-8
export LANG=zh_CN.UTF-8
##永久的就是写入到 /etc/profile最后即可
[root@shell /server/scripts]# source /etc/profile
[root@shell /server/scripts]# env |grep LANG
LANG=zh_CN.UTF-8
环境变量相关文件及目录
文件或目录 含义 /etc/profile 全局生效(国法) 存放函数,环境变量。存放别名 /etc/bashrc 全局生效(国法) 存放别名。 /etc/profile.d/ 目录,每个用户登录的时候(远程连接与su切换),加载目录下面.sh结尾 的文件.设置一些登录登录后提示,变化.
书写脚本每次用户登录后显示系统的基本信息。
#变量赋值
sys_hostname=`hostname` #主机名
sys_IP=`hostname -I` #ip地址
sys_total=`free -h |awk NR==2'{print $2}'` #总计内存
sys_free=`free -h |awk NR==2'{print $4}'` #可用内存
sys_load=`uptime |awk '{print $(NF-2),$(NF-1),$NF}'` #查看系统负载
##输出
cat <<EOF
主机名: ${sys_hostname}
ip地址: ${sys_IP}
总计内存: ${sys_total}
可用内存: ${sys_free}
系统负载: ${sys_load}
EOF
实现目标 软连接过去即可
ln -s /server/scripts/03-sys-info.sh /etc/profile.d/sys_info.sh
重新登录并测试
成功!
特殊变量
特殊变量-位置变量
位置的特殊变量 含义 $n(数字) 脚本的第n个参数 $0 脚本的名字 $# 脚本参数的个数
echo $1 $2
我们在写一个感受一下吧
echo "这是脚本的第一个参数: $1"
echo "这是脚本的第二个参数: $2"
echo "这是脚本的第三个参数: $3"
echo "脚本参数总数$#"
执行脚本的时候输入用户名,判断用户名是否存在
#变量
username= $1
#2处理
echo "正在检查$username" 是否存在
echo "请稍等"
sleep 5
id ${username}
echo "是否存在这个用户你自己看吧"
echo "看啥呢,no such没有用户"
echo "恭喜你会自己检查了"
如果脚本执行出错了,需要我们输出脚本的使 用帮助:脚本名字 {star|stop|restart}
echo "使用这个方法:$0 {start|stop|start}"
特殊变量-状态变量
$? ##上一个命令、脚本的返回值,0表示正确,非0即错误.
特殊变量-变量子串
${#parameter} ##统计字符长度(变量中有多少个字符)
${parameter#word} ##从变量左边开始删除,按照最短匹配删除
${parameter##word} ##从变量左边开始删除,按照最长匹配删除
统计字符串长
oldboy="xzb666"
echo ${#oldboy}
删除变量中的内容
oldboy="xzb666"
echo ${oldboy#xzb}
echo ${oldboy#*6}
截取(切片)
切片的时候,每个字符都有个位置,从0开始
o l d b o y 位置0 位置1 位置2 位置3 位置4 位置5
var=oldboy
echo ${var}
echo ${var:3} #从位置3开始输出
替换
var=xzbnb666
echo ${var/b/a} ##只替换第一个b
echo ${var//b/a} ##替换所有的b
1.3 变量赋值
##直接赋值 oldboy=xzb666
##命令结果赋值 hostname=`hostname`
##脚本传参 user_name=$1 脚本的第1个参数
##read交互式赋值 通过read命令实现
read通过交互式的方式进行赋值 -p 交互的时候提示信息。 -t 超过这个时间没有操作,则自动退出. -s 不显示用户的输入.记录密码才用.
基本用法
read -p "请输入密码:" pass
echo $pass
不显示用户的输入
read -s -p "请输入密码" pass
2. 运算方法
2.1 awk进行计算
基础用法
[root@shell /server/scripts]# awk 'BEGIN{print 1/3}'
0.333333
在awk中使用shell变量
[root@shell /server/scripts]# awk -vn1=1 -vn2=3 'BEGIN{print n1/n2}'
0.333333
awk -v选项用于创建或修改awk中的变量。-v是shell脚本与awk桥梁。
2.2 bc
-l 只显示小数
echo 1/3 |bc -l
2.3 expr
使用空格,对*号转义要用撬棍转义
2.4 计算器,传入脚本中2个参数,进行计算, 输出结果
num1=$1
num2=$2
#检查是否是数字
expr $num1 \* 0 + $num2 \* 0 + 1 &>/dev/null || {
echo "Uage: $0 数字1 数字2"
exit 1
}
echo "两个数字相加:`awk -vn1=$num1 -vn2=$num2 'BEGIN{ print n1+n2}'`"
echo "两个数字相减:`awk -vn1=$num1 -vn2=$num2 'BEGIN{ print n1-n2}'`"
echo "两个数字相乘:`awk -vn1=$num1 -vn2=$num2 'BEGIN{ print n1*n2}'`"
echo "两个数字相除:`awk -vn1=$num1 -vn2=$num2 'BEGIN{ print n1/n2}'`"
3. 条件表达式
条件表达式属于判断中的核心,if后面都在使用它
大部分常见的场景用 []判断
3.1 判断文件
条件表达式-文件 说明 -f 是否为文件 -d 是否为目录 -x 是否有执行权限 -s 文件是否存在,文件大小是否为0
##1. 条件测试语句,不会直接提示对错,通过返回值提示
[ -f /etc/hosts ]
echo $?
##2. 判断文件是否存在格式
[ -f /etc/hosts ] && echo "成立" || echo "失败"
[ -f /etchosts ] && echo "成立" || echo "失败"
执行流程:
&& 表示前面命令执行成功然后执行echo ‘成立 '
|| 表示前面命令执行失败了,echo ‘失败’
3.2 对比字符串
用于对比或比较2个字符串内容
条件表达式-字符串对比 说明 "str1" = "str2" str1 是否等于str2,如果相等则为真 "str1" != "str2" str1 是否等于str2,如果不相等则为真 -z "str" 检查str字符串是否为空,空的则为真。 -n "str" 检查str字符串是否为空,非空的则为真。
##对比两个字符串是否相等
input=start
[ "$input" = "start" ] && echo "成功" || echo "失败"
str1=xzb996
str2=cz996
[ "$str1" = "$str2" ] && echo "成功" || echo "失败"
[ "{$str1}x" = "{$str2}x" ] && echo "成功" || echo "失败"
##判断字符串是否为空
unset str2
echo $str2 '这个变量是空值'
[ -z "$str2" ] && echo "成功" || echo "失败"
[ -n "$str2" ] && echo "成功" || echo "失败"
3.3 比大小(整数)
-eq 等于
-gq 大于
-lq 小于
-ne 不等于
[ 666 -gt 1 ] && echo "成功" || echo "失败"
[ 666 -lt 1 ] && echo "成功" || echo "失败"
[ 666 -eq 1 ] && echo "成功" || echo "失败"
[ 666 -ne 1 ] && echo "成功" || echo "失败"
3.4 使用正则判断
[root@shell /server/scripts]# num=666
[root@shell /server/scripts]# [[ $num =~ [0-9] ]] && echo 成立 || echo 失败
成立
[root@shell /server/scripts]# num1=xzb666
[root@shell /server/scripts]# [[ $num1 =~ [0-9] ]] && echo "成立" || echo "失败"
成立 ##目前的正则,仅仅表示变量中只要有数字就行
[root@shell /server/scripts]# [[ $num1 =~ ^[0-9]+$ ]] && echo "成立" || echo "失败"
失败 ## 变量的值 开头结尾中间全是数字 连续数字
[root@shell /server/scripts]# [[ $num =~ ^[0-9]+$ ]] && echo "成立" || echo "失败"
成立
3.5 单分支判断(if)
##格式
if 条件;then
##满足条件后执行的内容。
fi
或者
if 条件
then
##满足条件后执行的内容。
fi
3.5.1 如果脚本参数个数不是2,则提示错误提示
if [ $# -ne 2 ];then
echo "脚本必要输入两个数字"
exit 1
fi
echo "恭喜你猜对了,进来吧 $1 $2"
3.6 双分支判断(if ..else)
if 条件;then
满足条件后执行的内容。
else
不满足条件执行的内容。
fi
3.6.1 检查根分区磁盘空间使用率
root_userage_now=`df -h |grep -w '/' |awk -F "[ %]+" '{print (NF-1)}'`
root_waring_now=4
##2.进行比较
if [ $root_userage_now -gt $root_waring_now ];then
echo "磁盘空间不足,使用率为$root_userage_now%"
else
echo "磁盘空间充足,使用率为$root_userage_now%"
fi
3.7 多分支判断(if...elif...else...)
比大小的改为,多分支格式
read -p "请输入变量": num1 num2
if [ $num1 -gt $num2 ];then
echo "$num1 >$num2"
elif [ $num1 -lt $num2 ];then
echo "$num1 <$num2"
else
echo "$num1=$num2"
fi
输出指定用户的信息
这个脚本未来可以用于做安全检查:
1.是否输入了用户名
2. 这个用户是否存在,如果不存在则退出,提示用户不存在。
3. 如果用户存在则输出下面的信息:
是否可以登录
用户uid,gid
用户家目录
最近1次登录情况
read -p "请输入用户名": user
id $user &>/dev/null
if [ $? -ne 0 ];then
echo "用户$user 不存在"
exit 1
fi
user_shell=`grep "^${user}:" /etc/passwd |awk -F: '{print $NF}'`
if [ "$user_shell" = "/bin/bash" ];then
echo "用户可以登录"
else
echo "用户不可以登录"
fi
user_uid=`grep "^${user}:" /etc/passwd|awk -F: '{print $3}'`
user_gid=`grep "^${user}:" /etc/passwd|awk -F: '{print $4}'`
user_homedir=`grep "^${user}:" /etc/passwd|awk -F: '{print $6}'`
user_lastlog=`lastlog |grep -w "${user}"`
##############
echo "用户uid: $user_uid"
echo "用户gid: $user_gid"
echo "用户家目录: $user_homedir"
echo "用户最后操作: $user_lastlog"
3.8 case语句
1. 条件分支语句,一般用于实现有多种选择的脚本
2. 这个功能:if或多分支if实现,如果使用case语句会更加清晰直观. 菜单。
格式:
case "变量" in
选择1)
命令;;
选择2)
命令;;
*)
命令(保底的默认输出);;
esac
某会所菜单展示程序
cat <<EOF
##############私人高级会所##########################
####################套餐############################
输入1,选择 138套餐) 吃饱套餐
输入2,选择 443套餐) 吃饱喝足套餐
输入3,选择 888套餐) 吃喝拉撒套餐
输入4,选择 1688套餐) 你想干啥就干啥套餐
输入其他内容,退出
EOF
##vars
read -p '请输入你的选择': num
##判断
case "$num" in
1) echo "138套餐) 吃饱套餐";;
2) echo "443套餐) 吃饱喝足套餐";;
3) echo "888套餐) 吃喝拉撒套餐";;
4) echo "1688套餐) 你想干啥就干啥套餐";;
*)
echo "选择错误"
esac
进阶格式:
1. 分支中可以使用|表示或者
2. *号表示所有
判断用户输入的是yes还是no
read -p "请选择yes or no:" choice
case "$choice" in
yes|YES|Yes|y|Y) echo "您输入的是yes";;
N|n|No|no) echo "您输入的是no";;
esac
4. 函数
4.1 概述
1. 在书写脚本的时候,尽量使用函数,规范脚本
2. 让脚本的开发,更加模块化
3. 方便后期调试,调试的时候只需要注释函数即可
4.2 格式
###定义方式01 完整写法
function xzb_show() {
命令
命令
内容
return n #函数的返回值
}
#定义方式02 精简写法一般使用这一种.
lidao_show() {
命令
命令
内容
return n #函数的返回值
}
4.3 使用
##定义函数
function show(){
echo "你的目标:拿下15k offer"
echo "你的目标:拿下100+ shell脚本"
}
##调用函数
show
4.4 函数传参
函数传参与脚本传参类似
位置参数 | shell脚本中 | 函数中 |
$n | 脚本的第n个参数 | 函数的第n个参数 |
$0 | 脚本的名字 | 脚本的名字 |
$# | 脚本的参数的格式 | 函数参数个数 |
$@/$* | 脚本所有的参数 | 函数所有的参数 |
function show(){
cat <<EOF
show函数的参数个数:$#
show函数的所有参数:$*
$1.com
$1.cn
$1.org
$1.icu
$1.com
$1.com
EOF
}
show xzb xzb-007
#show $1 ##这是脚本的第一个参数
4.5 服务管理
书写sersync服务管理脚本
如果用户输入的是start,则运行sersync启动的命令。
如果用户输入的是stop,则运行关闭sersync的命令。
如果用户输入的是status,则显示sersync是否运行中,pid。
如果用户输入的是restart,则运行stop的命令,然后运行start的命令。
如果用户输入的是其他的内容,则提示输入错误,提示格式
choice=$1
#定义函数
##1.1开启服务
sersync_start(){
sersync_count=`ps -ef |grep sersync2|grep -v grep|wc -l`
if [ $sersync_count -eq 0 ];then
sersync2 -rdo /app/tools/confxml.xml &>/dev/null
fi
}
##1.2 关闭服务
sersync_stop(){
sersync_count=`ps -ef |grep sersync2|grep -v grep|wc -l`
if [ $sersync_count -ne 0 ];then
sersync_pid=`ps -ef |grep sersync|grep -v grep|awk '{print $2}'`
kill $sersync_pid
fi
}
##1.3 重启服务
sersync_restart(){
sersync_stop
sersync_start
}
##1.4查看服务状态
sersync_status(){
sersync_count=`ps -ef |grep sersync2|grep -v grep|wc -l`
if [ $sersync_count -eq 0 ];then
echo "sersync is down "
else
echo "sersync is running"
fi
}
error_msg(){
echo "输入错误重新输入,请输入 {start|stop|restart|status}"
}
#调用函数
case "$choice" in
start) sersync_start ;;
stop) sersync_stop ;;
restart) sersync_restart ;;
status) sersync_status ;;
*) error_msg;;
esac
5. 端口检查
5.1 检查指定端口是否可以访问
url=$1
prot=$2
#检查
[ $# -ne 2 ] && {
echo "请输入 url和port"
exit 1
}
##检查端口
[[ $prot =~ ^[0-9]+$ ]] || {
echo "请输入有效端口"
exit 2
}
##正式检查
port_count=`nmap -p $port $url |grep -wc open`
if [ $port_count -eq 1 ];then
echo "$url $port 运行通畅"
else
echo $url $port 运行不通畅""
fi
5.2 检查指定url是否可以访问
url=$1
##检查
[[ $url=~^[0-9a-zA-Z./:]+$ ]] ||{
echo "请输入有效url"
exit 1
}
##正式检查
status_code=`curl -s -I $url |awk 'NR==1 {print $2}'`
[ -z "$status_code" ] && {
echo "$url is failed"
}
if [ "$status_code" -lt 400 ];then
echo "$url is ok"
else
echo "$url is failed"
fi
6. 循环
6.1 循环概述
循环类型 | 说明 |
---|---|
for循环 | 最常用的循环 |
while循环 当型循环 | while可以加入条件,死循环, 读取文件 |
do until 循环 直到循环 | 极少用 |
6.2 for 循环
猜测数字的脚本
guess_num=`echo $((RANDOM%101))`
count=0
check_percent(){
if [ $count -le 3 ];then
echo "你超越了99.99%的人"
elif [ $count -ge 4 -a $count -le 5 ];then
echo "你超越了80%的人"
else
echo "你超越了60的人"
fi
}
while :
do
read -p "请输入数字:" num
let count++
##判断是否是数字
[[ "$num" =~ ^[0-9]+$ ]] || {
echo "你输的不是数字"
continue
}
if [ $num -eq $guess_num ];then
echo "恭喜你猜对了!"
check_percent
exit 1
fi
if [ $num -gt $guess_num ];then
echo "不对哦,猜的数字太大了"
else
echo "不对哦,猜的数字太小了"
fi
done
7. 辅助
7.1 颜色
##Linux命令行给字体加颜色命令为:
echo -e "\E[1;31m红色字oldboy\E[0m"
##\E 或\033 表示要开启这种功能。
##[1;31m [效果;颜色m
##\E[0m 颜色设置结束
##1表示加粗,2正常的,5表示闪烁
##效果(1-10)
echo -e "\E[1;31m红色字oldboy\E[0m"
echo -e "\E[2;31m红色字oldboy\E[0m"
echo -e "\E[3;31m红色字oldboy\E[0m"
echo -e "\E[4;31m红色字oldboy\E[0m"
echo -e "\E[5;31m红色字oldboy\E[0m"
echo -e "\E[6;31m红色字oldboy\E[0m"
echo -e "\E[7;31m红色字oldboy\E[0m"
echo -e "\E[8;31m红色字oldboy\E[0m"
echo -e "\E[9;31m红色字oldboy\E[0m"
echo -e "\E[10;31m红色字oldboy\E[0m"
##颜色展示
for n in {30..50}; do echo -e "\E[1;${n}m ${n}oldboy\E[0m" ;done
##应用建议
##创建环境变量或写入脚本开头
export RED="\E[5;31m"
export GREEN="\E[1;32m"
export BLUE="\E[1;34m"
export END="\E[0m"
##永久使用/etc/profile中即可。
##使用
echo -e "${RED}这是红色{}END"
echo -e "${BLUE}这是蓝色{END}"
echo -e "${GREEN}这是绿色{END}"
##写为函数
redecho() {
echo -ne "\e[5;31m"
echo -n "$@"
echo -e "\e[0m"
#echo -e "\e[5;31m $@ \e[0m"
}
greenecho() {
echo -ne "\e[1;32m"
echo -n "$@"
echo -e "\e[0m"
}
blueecho() {
echo -ne "\e[1;34m"
echo -n "$@"
echo -e "\e[0m"
}