Shell编程语法及应用

简介

Shell脚本与Windows/Dos下的批处理相似,也就是用各类命令预先放入到一个文件中,方便一次性执行的一个程序文件,主要是方便管理员进行设置或者管理用的。但是它比Windows下的批处理更强大,比用其他编程程序编辑的程序效率更高,它使用了Linux/Unix下的命令

变量

变量类型
本地变量:当前用户进程
环境变量:当前用户进程和子进程(env/set)
全局变量:所有用户程序都能调用
系统变量:内置bash变量
	$?:上一条命令执行返回状态;0表示执行正常,非0表示执行不正常
	$#:表示脚本接的参数个数
	$*:表示脚本后面所有参数
	$@:表示脚本后面所有参数
	$0:运行脚本的名称
	${1}-${9}:表示脚本后面的位置参数
	$$:当前所在进程的进程号
	$!:最后一个放在后台运行的进程号
	!$:调用最后一条命令的参数
	$USER:显示当前用户信息
	$UID:显示当前用户uid
	$HOME:显示当前用户家目录
只读变量
	readonly variable
删除变量
	unset variable
定义有类型变量,对变量做限制
declare [options] 变量名=变量值
常用选项
-i:声明一个整型
-r:声明一个readonly变量,该变量的值无法改变,并且不能为unset
-a:声明一个数组
-x:声明环境变量

字符串

#shell编程中字符串可以用单引号,也可以用双引号,也可以不用引号
#单引号字符串的限制:1)单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;2)单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用

用法
#截取字符串
A=123456
echo ${A:2:3}=>345
#删除字符串的一部分(从后往前删)
${string%substring}
#拼接字符串
string="var1"$var2"var3"
#获取字符串长度
${#string}
#查找子字符串(找i或o的位置,以先出现的字符为准)
echo `expr index $string io`
#字符串内容删除和替换
url=www.haha.com
左往右去掉一个key:${url#*.}=>haha.com
左往右去掉最大程度去掉:${url##*.}=>com
右往左去掉一个key:${url%.*}=>www.haha
右往左去掉最大程度去掉:${url%%.*}=>www
#字符串运算
=:检测两个字符串是否相等,相等返回true
!=:检测两个字符串是否相等,不相等返回true
-z:检测字符串长度是否为0,为0返回true
-n:检测字符串长度是否不为0,不为0返回true
$:检测字符串是否为空,不为空返回true
#字符串比较
\>:检查字符串大小关系,左边大于右边则为true
\<:检查字符串大小关系,左边小于右边则为true

数组

普通数组(整数作为下标)
	分别定义:array[0]=v1 array[1]=v2 ...
	连续定义:array=(var1 var2 ... varn)
	array=(`cat /etc/passwd`)通过空格分隔
	查看单个元素:${array[0]}
	查看所有元素:
		${array[*]}
		${array[@]}
	截取部分元素:${array[*]:1:3}
	查看元素个数:${#array[*]}
	获取元素下标:
		${!array[*]}
		${!array[@]}
关联数组(可使用字符串作为下标,先声明在定义)
	定义:declare -A array
	查看关联数组 declare -A
	分别定义:array[a]=v1 array[b]=v2 ...
	连续定义:array=([name1]=var1 [name2]=var2 ... [namen]=varn)
	查看单个元素:${array[a]}
	查看所有元素:
		${array[*]}
		${array[@]}
	截取部分元素:${array[*]:1:3}
	查看元素个数:${#array[*]}
	获取元素下标:
		${!array[*]}
		${!array[@]}
	运算(默认为0):
		let array[name1]++
		let array[name1]+=num
清空数组
	unset array[0]:清空第一个值
	unset array:清空整个数组

用法

魔法字节(通配)

#!/bin/env bash

查看运行过程

[root@localhost ~]# bash -x my.sh

查看进程ID

[root@localhost ~]# pgrep 进程名称 &> /dev/null

查看语法问题

[root@localhost ~]# bash -n my.sh

date工具

前一分钟(日周月年)
	date -d '-1 minute'
后一分钟
	date -d '1 minute'
设置格式
	date +%d/%b%Y:%H%M

语法

read方法

#从文件读取变量
read -p "Input:" name  < filename
#隐藏读取变量
read -s -p "Please enter your password" pass
#设置读取超时
read -t -p "Please Input data:" data
#设置读取输入次数(次数为n后的数字)
read -n1 -p "Please Input data" data
#读取多个参数,空格隔开
read -p "Input your name(firstname lastname)" first last

按行读取文件内容

方法一
cat test.txt|while read line
do
	xx
done
方法二
while read line 
do
	xx
done < test.txt

算术运算(仅整数)

$((1+1))
$[10-5]
expr 1 + 1(不可求幂)
n=1;let n=n+1;echo $n(需先定义变量)
#比较
$a == $b:相等
$a != $b:不相等

关系运算符

-eq:检测两个数是否相等,相等返回true
-ne:检测两个数是否不相等,不相等返回true
-gt:检测左边的数是否大于右边,如果是,则返回true
-lt:检测左边的数是否小于右边,如果是,则返回true
-ge:检测左边的数是否大于等于右边,如果是,则返回true
-le:检测左边的数是否小于等于右边,如果是,则返回true

布尔算术

!:非运算,表达式为true则返回false,否则返回true
-o:或运算,有一个表达式为true则返回 true
-a:与运算,两个表达式都为true才返回true

文件比较

-e:判断对象是否存在
-d:判断对象是否存在,并且为目录
-f:判断对象是否存在,并且为常规文件
-L:判断对象是否存在,并且为符号链接
-h:判断对象是否存在,并且为软链接
-s:判断对象是否存在,并且长度不为0
-r:判断对象是否存在,并且可读
-w:判断对象是否存在,并且可写
-x:判断对象是否存在,并且可执行
-O:判断对象是否存在,并且属于当前用户
-G:判断对象是否存在,并且属于当前用户组
-nt:判断file1是否比file2新
-ot:判断file1是否比file2旧

取出当前文件的名称

basename $0

shift

#使位置参数左移(默认1位),也可以设定为多位
#输入参数one two three four five six
#shift就是右移拿掉多少个参数的意思
echo "The script name is ==> ${0} "
echo "Total parameter number is ==> $# "
echo "Your whole parameter is ==> '$@' "
shift #进行一个变量的shift
echo "Total parameter number is ==> $# "
echo "Your whole parameter is ==> '$@' "
shift 3 #第二次进行三个变量的shift
echo "Total parameter number is ==> $# "
echo "Your whole parameter is ==> '$@' "

并发执行

{}&包住函数体,加入wait关键字

实现输出

cat << -EOF
xx
EOF

随机数(0-32767)

$RANDOM
产生0-n-1之间随机数
	$[$RANDOM%n]
产生10-99
	$[$RANDOM%90+10]

expect(实现自动应答)

yum install expect -y

vi test.sh
#!/usr/bin/expect
#位置参数定义变量
#外部传参数
set ip [ lindex $argv 0 ]
set pass xxx
#开启一个程序
spawn ssh root@$ip
#捕获相应内容
#exp_continue参数表示可忽略此捕获
expect {
	"(yes/no)?" { send "yes\r";exp_continue }
	"password:" { send "$pass\r";exp_continue }
}
#进行操作
expect "#"
send "rm -rf /tmp/*\r"
send "hostname\r"
send "date +%F\r"
send "touch file{1..3}\r"
send "ls /tmp/\r"
expect eof

chmod +x test.sh
./test.sh ip

getopt用法

#用法:
	getopts 选项 参数
	注:选项后加:则选项必须带参数,若选项后不带:则可以不带参数
#实例
#!/bin/bash
while getopts a:b:c:def opt
do
	case "$opt" in
	a)echo "Found the -a option";;
	b)echo "Found the -b option";;
	c)echo "Found the -c option";;
	d)echo "Found the -d option";;
	e)echo "Found the -e option";;
	f)echo "Found the -f option";;
	*)echo "Unknown option :$opt";;
	esac
done

捕捉信号

用法
trap "命令(可选)" 信号(多个用空格相隔)
#捕捉ctrl-c信号并提示
trap "echo 'Sorry! I have trapped Ctrl-C'" SIGINT SIGTERM
#捕捉脚本退出并提示
trap "echo bye" EXIT
#移除捕捉
trap -EXIT

条件判断

语法
	test 条件
	[ 条件 ]
	[[ 条件 ]]
类C风格
	数值比较:(( 条件 ))
[][[]]区别:
	判断空字符串时,[]需要对变量加"";[[]]不需要
	&&和||,[]要分开多个条件,[[]]不需要

流程控制

if结构

语法
	if [[ condition1 ]];then
		command1
	  elif [[ condition2 ]];then
		command2
	  else
		command3
	fi 

for结构

语法
	带列表:
	for variable in $list
	   do	
		command
	   done
	不带列表(由用户输入的参数决定,$1$2):
	for variable in "$@"
	类C风格:
	for (( expr1;expr2;expr3 )) 

while结构

语法
	while 表达式
	  do
		command
	  done

until结构

语法
	until expression
	  do
		command
	  done

case语句

语法
	case var in
	pattern 1)
		command
		;;
	pattern 2)
		command
		;;
		*)#相当于default
		command
		;;
	esac

select循环(方便输出菜单,无限循环)

语法
	#定义提示符,$PS1,$PS2,$PS3
	PS3="Your choice is:"
	select choice in xxx xxx xxx
	do
		case "$choice" in
			xxx)
		esac
	done

函数

语法
	[function] 函数名(){
		函数体
		打印传给函数的第一个参数:echo $1
	}
调用
	终端临时调用:source function.sh;function_name
	环境变量:函数写入/etc/bashrc(用户在家目录.bashrc)
	脚本中调用:function_name(脚本中定义|source function.sh)
返回值
函数最后一条命令执行成功默认返回状态码为0
函数最后一条命令执行不成功则返回状态码为非0

函数返回

echo输出内容
function f{
	read -p "Enter a value:" value
	echo $[ $value*2 ]
}
result=`db1`
echo "The new value is $result"

return返回值
function f{
	read -p "Enter a value:" value
	echo "doubling the value"
	return $[ $value * 2 ]
}
db1
echo "The new value is $?"

函数传参

function f{
	# $1和$2 不能从命令行中传递,只能在调用函数时手动进行传递
	echo $[ $1 * $2 ]
}
var1=3	
var2=5
value=`f $var1 $var2`
echo "The result is $value"

向函数传递数组

function f {
	echo "The parameters are : $@"
	
	#函数只会读取数组变量的第一个值
	thisarray=$1
	echo "The received array is ${thisarray[*]}"
	
	newarray=(`echo "$@"`)
	echo "The new array value is : ${newarray[*]}"
}
myarray=(1 2 3 4 5)
echo "The original array is : ${myarray[*]}"
#将数组变量当成一个函数参数,函数只会去函数变量第一个值
#f $myarray
#向函数传递数组的所有元素函数才可读取整个数组
f ${myarray[*]}

应用

需求:随机产生1000个139开头的电话号码,并抽取五位幸运观众

n1=0
n2=0
n3=0
n4=0
n5=0
n6=0
n7=0
n8=0
file=/tmp/phonenum.txt
for ((i=1;i<=1000;i++))
do
	for ((j=1;j<=8;j++))
	do
	let n$j=$[$RANDOM%10]
	done
	echo "139$n1$n2$n3$n4$n5$n6$n7$n8" >> $file 
done
#!/bin/env bash
#抽取五个幸运观众
phone=/tmp/phonenum.txt
for ((i=1;i<=5;i++))
do
	line=`wc -l $phone|cut -d ' ' -f1`
	lucky_lines=$[$RANDOM%$line+1]
	#取出号码
	luck_num=`head -$lucky_lines $phone |tail -1`
	#替换显示到屏幕
	echo "139****${luck_num:7:4}"
	echo $luck_num >> /tmp/luck.txt
	#删除已抽取的号码,sed用shell变量要用双引号
	sed -i "/$luck_num/d" $phone
done

需求:保留最近3天的日志

#!/bin/bash
#实现一:
#定期删除目录下修改时间大于7天的文件,使用绝对路径
#脚本加上i权限防止误操作
#方法一:
`find /path -mtime +7 -exec rm -r {} \;`
#方法二:
`find /path -mtime +7 |xargs rm -rf `

#实现二:
#一直保留前两个工作日的备份文件,使用绝对路径
#方法一:
`ls -t /path/*.tar.gz | awk 'NR>2' |xargs rm -rf `
#方法二:
`ls -t /path/*.tar.gz|awk 'NR>2 {print "rm -f "$0""}'|bash`

需求:统计网站连接状态

#!/bin/bash
declare -A state_array
states=`ss -ant|grep :80 |cut -d ' ' -f1`
for i in $states
do
	let state_array[$i]++
done

for j in ${!state_array[*]}
do
	echo $j:${state_array[$j]}
done

需求:脚本查看系统性能

#!/bin/bash
PS3="Your choice is:"
#判断系统
os_check(){
	if [ -e /etc/redhat-release ];then
		REDHAT=`cat /etc/redhat-release|cut -d ' ' -f1`
	else
		DEBIAN=`cat /etc/issue|cut -d ' ' -f1`
	fi
	
	if [ "$REDHAT" == "Centos" -o "$REDHAT" == "Red" ];then
		P_M=yum
	elif [ "$DEBIAN" == "Ubuntu" -o "$DEBIAN" == "ubuntu" ];then
		P_M=apt-get
	else
		echo "Operating system does not support."
		exit 1
	fi
}

if [ $LOGNAME != root ];then
	echo "Please use the root account operation."
	exit 1
fi

instll_software(){
if ! which $1  &> /dev/null;then
	echo $1 "command not found,now the install."
	sleep 1
	os_check
	$P_M install $2 -y
	echo "--------------------"
fi
}

while true
do
	select input in cpu_load disk_load disk_use disk_inode mem_use tcp_status cpu_top10 mem_top10 traffic quit
	do
		case $input in
			cpu_load)
				#CPU利用率和负载
				echo "-------------------------"
				i=1
				while [[ $i -le 3 ]]
				do
					#定义颜色
					echo -e "\033[32m 参考值${i}\033[0m"
					UTIL=`vmstat|awk '{if(NR==3)print 100-$15"%"}'`
					USER=`vmstat|awk '{if(NR==3)print $13"%"}'`
					SYS=`vmstat|awk '{if(NR==3)print $14"%"}'`
					IOWAIT=`vmstat|awk '{if(NR==3)print $16"%"}'`
					echo "Util:$UTIL"
					echo "User use:$USER"
					echo "System use:$SYS"
					echo "I/O wait:$IOWAIT"
					let i++
					sleep 1
				done
				echo "-------------------------"
				break
			;;
			disk_load)
				#硬盘I/O负载
				echo "-------------------------"
				i=1
				while [[ $i -le 3 ]]
				do
					echo -e "\033[32m 参考值${i}\033[0m"
					UTIL=`iostat -x -k|awk '/^[v|s]da/{print $1,$NF"%"}'`
					READ=`iostat -x -k|awk '/^[v|s]da/{print $1,$4"KB"}'`
					WRITE=`iostat -x -k|awk '/^[v|s]da/{print $1,$5KB"%"}'`
					IOWAIT=`vmstat|awk '{if(NR==3)print $16"%"}'`
					echo "Util:$UTIL"
					echo "Read:$READ"
					echo "Write:$WRITE"
					echo "I/O wait:$IOWAIT"
					let i++
					sleep 1
				done
				echo "-------------------------"
				break
			;;
			disk_use)
				echo "------------------"
				#硬盘利用率
				DISK_TOTAL=`fdisk -l|awk  '/dev\/sda/&& /^Disk.*字节/{print $2"GB"}'`
				USE_RATE=`df |awk '/\/dev\/mapper/{print $5}'`
				for i in $USE_RATE
				do
					flag=`awk -v num1="$i" -v num2=90% 'BEGIN{print (num1<=num2)?"0":"1" }'`
					if [ $flag -eq 1 ];then
						PART=`df -h|awk '{if(int($5)=='''$i''') print $6}'`
						echo "$PART=${i}%"
					fi
				done
				echo "Disk total:${DISK_TOTAL}"
				echo "Use rate:${USE_RATE}"
				echo "--------------------"
				break
			;;
			disk_inode)
				echo "------------------"
				#硬盘inode利用率
				USE_RATE=`df -i |awk '/\/dev\/mapper/{print $5}'`
				for i in $USE_RATE
				do
					flag=`awk -v num1="$i" -v num2=90% 'BEGIN{print (num1<=num2)?"0":"1" }'`
					if [ $flag -eq 1 ];then
						PART=`df -h|awk '{if(int($5)=='''$i''') print $6}'`
						echo "$PART=${i}%"
					else
						echo "UseRate:"${USE_RATE}
						echo "Inode use rate no than 90% of the partition."
					fi
				done
				echo "------------------"
				break
			;;
			mem_use)
				echo "------------------"
				MEM_TOTAL=`free -m|awk '{if(NR==2)printf"%.1f",$2/1024}END{print"G"}'`
				USE=`free -m|awk '{if(NR==2)printf"%.1f",$3/1024}END{print"G"}'`
				FREE=`free -m|awk '{if(NR==2)printf"%.1f",$4/1024}END{print"G"}'`
				CACHE=`free -m|awk '{if(NR==2)printf"%.1f",$6/1024}END{print"G"}'`
				echo "Total:${MEM_TOTAL}"
				echo "Use:${USE}"
				echo "Free:${FREE}"
				echo "Cache:${CACHE}"
				echo "------------------"
				break
			;;
			tcp_status)
				#网络连接状态
				echo "------------------"
				COUNT=`ss -ant|awk '!/State/{status[$1]++}END{for (i in status)print i,status[i]}'`
				echo "Tcp connect status:\n$COUNT"
				echo "------------------"
				break
			;;
			cpu_top10)
				#CPU占用率前10进程
				echo "------------------"
				CPU=`ps -axu|awk '{if($3>0.1) print "PID: "$2" CPU: "$3"% --> "$NF"\n"}'|sort -k4 -nr|head -10`
				num=0
				for i in $CPU
				do
					echo -n "$i"
					let num++
					if [ $num -eq 6 ];then
						echo
						let num=0
					fi
				done
				echo "------------------"
				break
			;;
			mem_top10)
				#内存占用前10进程
				echo "------------------"
				MEM=`ps -axu|awk '{if($4>0.1){print "PID: "$2" Memory: "$4"% --> "$NF""}}'|sort -k4 -nr|head -10`
				num=0
				for i in $MEM
				do
					echo -n "$i"
					let num++
					if [ $num -eq 6 ];then
						echo
						let num=0
					fi
				done
				echo "------------------"
				break
			;;
			traffic)
				#网络流量
				while true
				do
				read -p "Input network cat name (ens[33/37]):" ens
				#判断是否存在
				if [ `ifconfig |grep -c "\<$ens\>"` -eq 1 ];then
					break
				else
					echo "Input error,please input again."
				fi
				done
				echo "---------------------------"
				OLD_RX=`ifconfig ens33 |awk '{if(NR==5)print $5}'`
				OLD_TX=`ifconfig ens33 |awk '{if(NR==7)print $5}'`
				sleep 1
				NEW_RX=`ifconfig ens33 |awk '{if(NR==5)print $5}'`
				NEW_TX=`ifconfig ens33 |awk '{if(NR==7)print $5}'`
				IN=`awk 'BEGIN{printf "%.1f\n",'$((${NEW_RX}-${OLD_RX}))'/1024/1024}'`
				OUT=`awk 'BEGIN{printf "%.1f\n",'$((${NEW_TX}-${OLD_TX}))'/1024/1024}'`
				echo "IN:${IN}MB/s OUT:${OUT}MB/s"
				echo "---------------------------"
				break
			;;
			quit)
				exit
			;;
			*)
				echo "---------------------------"
				echo "Input number"
				break
				echo "---------------------------"
			;;
		esac
	done
done
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值