shell编程:
一、知识点:
1、相关概念:
(1)脚本编程
#bash -n SCRIPT_FILE(判断脚本是否有语法错误)
#bash -x SCRIPT_FILE(判断脚本是否有语法错误,并详细列出执行的每一个步骤)
#basename /root/sum.sh(strip directory and suffix from filenames直接取路径的基名(文件名),脚本中常用#basename $0)
编译器、解释器
(2)编程语言:机器语言、汇编语言、高级语言
静态语言:编译型语言、强类型(变量在使用前必须事先说明,甚至还需初始化)、执行前先编译,要事先转换好,如:C、C++、JAVA、C#;
动态语言:解释型语言、弱类型(变量用时声明,甚至不区分类型)、边解释边执行(ON THE FLY),如:ASP、PHP、SHELL、PYTHON、PERL;
面向过程:LINUX的内核是面向过程的C语言,SHELL脚本;
面向对象:JAVA、PYTHON;
注:PERL、C++既面向过程,也面向对象;
(3)生成随机数:#echo $RANDOM,bash内部自动维护的,生成数在0-32768之间;随机数生成器:/dev/random,/dev/urandom,默认都到熵池中去取随机数,若被取空,ramdom会停下来等待生成更多的随机数,所以会阻塞用户,urandom会用软件自动生成。
2、变量:内存空间(编址的存储单元);
十进制数10:字符型存:16bit;数值型存:8bit;
变量类型(事先确定数据的存储格式和长度):字符型、数值型(整型、浮点型)、布尔型(真假);
整型:8bit,0-255,若存256则会溢出;缓冲区溢出:(精心设计的溢出——网络攻击);
变量名称:只能包含字母、数字、下划线,且不能以数字开头;不能与系统中存在的环境变量重名;最好做到见名知义;
变量赋值:VAR_NAME=VALUE
逻辑运算:与&、或|、非!、异或^
短路逻辑运算:知道其中一个就知道其结果,如:对于与,其中一个为假,结果就为假;对于或,其中一个真,结果就为真;
3、bash变量类型:环境变量、本地变量(局部变量)、位置变量($1,$2,...)、特殊变量($?,$*,$#,$@);
环境变量:作用域为当前SHELL进程及其子进程;脚本在执行时会启动一个子SHELL进程;命令行中启动的脚本会继承当前SHELL环境变量;系统自动执行的脚本(非命令行启动)就需要自我定义需要的各环境变量;#export VAR_NAME=VALUE赋值并声明为环境变量;
本地变量:作用域为整个BASH进程;
局部变量:作用域为当前代码段;
位置变量:$1,$2,$3,....引用脚本的参数;
特殊变量:$?(上一个命令执行状态的返回值),程序执行完,可能有两类返回值(程序的执行结果、程序状态返回代码(其中0为正确执行,1-255为错误执行)),写脚本时可自定义程序退出的代码;127为命令错误,2为参数错误;$@和$*,$#(参数的个数)
#id root &> /dev/null
#echo $?
引用变量:${VAR_NAME},花括号有时可省去,但下列情形花括号不能省,
#animal=pig
#echo “There are some ${animal}s.” (注意单双引号的区别)
撤销变量:#unset VAR_NAME (不是操作变量的值,就不需加$)
查看变量:#set (查看当前SHELL所有变量)
#printenv (以下三个都是查看环境变量)
#env
#export
4、脚本:命令的堆砌,按实际需要,结合命令流程控制机制实现的源程序
#!/bin/bash(此句必须是文件的第一行,shabang魔数)
#which bash
#file /bin/ls (ELF可执行可链接的文件格式)
执行脚本:一、赋予可执行权限,用绝对路径执行;二、#bash 脚本文件
条件测试类型:整数测试、字符串测试、文件测试
条件测试的表达式:[ expression ]、[[ expression ]]、test expression
整数测试:-eq,-ne,-gt,-lt,-ge,-le
字符串测试:==,!=,-n string(空为真),-z string(不空为真)
文件测试:-e,-f,-d,-r,-w,-x
#bash -n script.sh (测试脚本是否有语法错误)
#bash -x script.sh (测试脚本是否有语法错误,每一步执行都显示出来)
命令间的逻辑关系:逻辑与&&、逻辑或||;
&&:前一个语句假后一个语句不执行,前一个语句真后一个语句执行;
||:前一个语句假后一个语句执行,前一个语句真后一个语句不执行;
举例:
#! id user1 && useradd user1 || echo “user1 exists!”
#! id user1 && useradd user1 && echo “user1” | passwd --stdin user1 || echo “user1 exists”
组合测试条件:
-a(与关系)
-o(或关系)
!(非关系-not)
举例:[ ! \( $1 == ‘q’ -o $1 == ‘quit’ -o $1 == ‘Q’ \) ]
算术运算:
#A=3
#B=6
#let c=$A+$B (第一种方式,#help let)
#d=$[$A+$B] (第二种方式)
#e=$(($A+$B)) (第三种方式)
#f=`expr $A + $B` (第四种方式,expr算术运算表达式,表达式中各操作数及运算符之间要有空格,而且要使用命令引用,#g=$(expr $A + $B)
注:shell默认所有类型均为字符串,不能做算术运算。
常用的赋值表达式:
i+=1(类似于i++;i-=1(i--);i*=1;i/=1;i%=1)
++i;--i
5、脚本编程知识点:
(1)顺序结构(按行依次写命令即可,若一行执行多个语句用分号分隔)
(2)选择结构:if单分支、双分支、多分支;case语句
(3)循环结构:for;while;until
(4)函数
(5)截取路径:#man bash
${parameter#*word}从左至右截取;
${parameter##*word};
${parameter%*word}从右至左截取;
${parameter%%*word};
例如1:#FILE=’/usr/local/src’
#echo ${FILE#*/}结果为/usr/local/src
#echo ${FILE##*/}结果为src
#echo ${FILE%/*}结果为/usr/local,此项常用,用于自动复制命令及命令的库文件的脚本
#echo ${FILE%%/*}结果为空
例如2:#A=`stty -F /dev/console size`显示屏幕大小
#echo ${A#* }
#echo ${A% *}
(6)#echo ${#VARNAME}列出变量所包含的的字符串长度
例如:#echo ${#FILE}结果为14
(7)变量赋值:
${parameter:-word}如果parameter为空或未定义,则变量展开为word;否则展开为parameter的值;
${parameter:+word}如果parameter为空或未定义,不做任何操作;否则展开为word值;
${parameter:=word}如果parameter为空或未定义,则变量展开为word;并将展开的值赋给parameter;
${parameter:offset}
${parameter:offset:length}取子串,从offset处的后一个字符开始,取length长度的子串。
例如:#echo ${A:-30}(A为空,则展开为30)常用
#echo ${A:+30}(A为空则空,此项可用来判断A是否有数据)
#echo ${A:=30}(A为空,30赋值给A)
#A=’hello world’
#echo ${A:2}(显示结果为llo world)
#echo ${A:2:3}(显示结果为llo)
(8)/etc/rc.d/init.d/服务脚本
/etc/sysconfig/服务脚本同名的配置文件
(9)局部变量:
local VARNAME=VALUE
比较有local和无local的差别,变量的作用域之间是如何影响的,例如:
#!/bin/bash
#
a=1
test() {
local a=$[3+4] #有local仅在当前函数中生效
}
test
(10)#mktemp FILE|DIR(创建临时文件或目录,脚本中防止创建的文件重名,可用X代替随机数的个数)
例如:#mktemp /tmp/file.XXXX(X为随机数的个数)
#mktemp -d /tmp/dir.XXXXX(-d创建目录)
#FILE=`mktemp /tmp/file.XXXXXXX`
#eecho $FILE
(11)信号:
#kill -l(Print a list of signal names)
常用信号:
-SIGHUP(hangup,1)
-SIGINT(interrupt,2,ctrl+c,中断前台正在运行的进程)
-SIGKILL(9)
-SIGTERM(termination,15)
-SIGCONT(continue,18,#bg %JOBID,将后台停止的作业让其继续运行)
-SIGSTOP(19,ctrl+z将前台作业放至后台运行)
脚本中可实现信号捕捉,但9和15信号无法捕捉。
#trap ‘command’ SIGNAL
例如:
#!/bin/bash
#
trap ‘echo “you go…”’ INT
while :;do
date
sleep 2
done
(12)数组array(连续的内存空间,元素element(数据中每一个独立的变量),每一个元素用index标示)
#declare -a AA(声明一个数组)
数组赋值方法一:
#declare -a AA
#AA[0]=jerry
#AA[1]=tom
#AA[2]=wendy
#AA[6]=natasha
#AA[4]=”nikita black”(值中包含空格要用引号)
数组赋值方法二:
#declare -a AA
#AA=(jerry tom wendy)
#AA=([0]=jerry [1]=tom [2]=wendy [6]=natasha [7]=”nikita black”)
#echo ${AA[0]}(查看数组中某元素的值)
#echo ${AA[2]}
#echo ${#AA[6]}(查看数组中某元素的值的字符个数)
#echo ${AA[*]}(查看数组中所有元素的值)
#echo ${#AA[*]}(查看数组中值不为空的元素的个数)
#echo ${#AA[@]}(查看数组中值不为空的元素的个数)
(13)脚本中使用选项(getopts,获取bash 脚本的选项及选项后的参数,并在脚本中调用,格式如下)
#man getopts
#help getopts
getopts optstring name [arguments]
注:optstring表示可用的选项都有哪些,仅支持短选项,默认只获取一个选项
$OPTARG、$OPTIND(这是getopts内置的两个变量,$OPTARG表示选项后参数的值;$OPTIND表示选项的索引index,永远指向下一个,如有一个选项时该值是2,有两个选项时该值是3)
shift $[$OPTIND-1](表示踢走前面的选项)
getopts “b:d:” OPT(b和d后的冒号表示选项后必须要跟参数,否则会报错)
getopts “:b:d:” OPT(第一个冒号表示不输出错误信息,这个错误信息是getopts的错误信息而不是脚本)
使用方法:
while getopts “:d:” OPT;do
case $OPT in
d)
echo “the option is d”
echo $OPTARG
;;
*)
echo “Usage: ……”
;;
esac
done
具体用法见以下(举例21和22)
(2)条件判断,控制结构:
单分支if语句:
if 判断条件;then
statement1
statement2
......
fi
双分支if语句:
if 判断条件;then
statement1
statement2
......
else
statement3
statement4
......
fi
多分支if语句:
if 判断条件1;then
statement1
......
elif 判断条件2;then
statement2
......
elif 判断条件3;then
statement3
......
else
statement4
......
fi
case 变量 in
value1)
statement
;;
value2)
statement
;;
*)
statement
;;
esac
(3)循环:进入条件,退出条件,for,while,until
for 变量 in 列表;do
循环体
done
生成列表的方式:{1..100};seq [起始数 [步进数] 尾数;
整个语句将列表遍历完成之后退出;
for ((expr1;expr2;expr3));do
循环体
done
注:此种写法近似C语言风格,其中expr1表示初始值,expr2表示退出条件,expr3表示修正变量值)
while CONDITION;do(条件满足进入循环,条件不满足退出循环,适用于循环次数未知的场景,注意要有退出条件)
statement
done
until CONDITON;do(条件不满足进入循环,条件满足退出循环)
statement
done
break(提前退出循环,可用来控制死循环)
continue(提前结束本轮循环,而进入下一轮循环)
while特殊用法一(死循环,需break控制退出):
while :;do
statement
done
while特殊用法二:
while read LINE;do
statement
done < /PATH/TO/SOMEFILE
(4)函数:
功能function,把执行的一段代码封装成一个独立的功能,并给之取个名字,需要时直接调用(调用时直接写函数名)
结构化编程的重要思想,主要目的实现代码重用,不能独立运行,需要调用时执行,可以被多次调用;库文件.so能被多个进程调用
定义一个函数,两种方式,函数名可以是字母数字下划线,不能数字开头:
function FUNCNAME{
COMMAND
]
FUNCNAME() {
COMMAND
}
自定义执行状态返回值:return #(退出函数,0-255)
exit #(退出脚本)
二、操作:
举例1:
#!/bin/bash
#使用脚本添加或删除用户
if [ $# -lt 1 ]; then
echo “Usage: adminusers ARG.”
exit 7
fi
if [ $1 == ‘add’ ];then
for I in {1..10};do
if id user$I &> /dev/null;then
echo “user$Iexists.”
else
useradd user$I
echo “user$I” | passwd --stdin user$I &> /dev/null
echo “Add user$I finished.”
fi
done
elif [ $1 == ‘del’ ];then
for I in {1..10};do
if id user$I &> /dev/null;then
userdel -r user$I
echo “Delete user$I finished.”
else
echo “No user$I.”
fi
done
else
echo “Unknown ARG.”
exit 8
fi
-------------------------------------------------------------------------------------------------------------
举例2:
#!/bin/bash
#打印1到100偶数和与奇数和
declare -i EVENSUM=0
declare -i ODDSUM=0
for I in {1..100};do
if [ $[$I%2] -eq 0 ];then
let EVENSUM+=$I
else
ODDSUM+=$I
fi
done
echo “Oddsum is $ODDSUM.”
echo “Evensum is $EVENSUM.”
-------------------------------------------------------------------------------------------------------------
举例3:
#!/bin/bash
#打印1到100的和
declare -i SUM=0
for ((I=1;I<=100;I++));do
let SUM+=$I
done
echo “1+2+..100=$SUM.”
-------------------------------------------------------------------------------------------------------------
#!/bin/bash
#
declare -i SUM=0
for I in {1..100};do
let SUM+=$I
done
echo “1+2+..100=$SUM.”
-------------------------------------------------------------------------------------------------------------
#/bin/bash
#
declare -i SUM=0
for I in `seq 1 1 100`;do
let SUM+=$I
done
echo “1+2+...100=$SUM.”
-------------------------------------------------------------------------------------------------------------
#!/bin/bash
#
decalre -i SUM=0
declare -i I=0
while [ $I -le 100 ];do
let SUM+=$I
let I++
done
echo “1+2+...100=$SUM.”
-------------------------------------------------------------------------------------------------------------
#!/bin/bash
#
declare -i I=0
declare -i SUM=0
until [ $I -gt 100 ];do
let SUM+=$I
let I++
done
echo “1+2+...100=$SUM.”
-------------------------------------------------------------------------------------------------------------
举例4:
#!/bin/bash
#检测用户hadoop是否在线,若在线则打印至屏幕并退出
until who | grep “hadoop” &> /dev/null;do(此句也可写为while ! who | grep “hadoop” &> /dev/null;do)
echo “hadoop is not come!”
sleep 5
done
echo “hadoop is logged in.”
-------------------------------------------------------------------------------------------------------------
举例5:
#!/bin/bash
#随机生成10个数,比较最大数最小数
declare -i MAX=0
declare -i MIN=0
for I in {1..10};do
MYRANDOM=$RANDOM
[ $I -eq 1 ] && MIN=$MYRANDOM
if [ $I -le 9 ];then
echo -n "$MYRANDOM,"
else
echo "$MYRANDOM"
fi
[$MYRANDOM -gt $MAX ] && MAX=$MYRANDOM
[$MYRANDOM -lt $MIN ] && MIN=$MYRANDOM
done
echo $MAX,$MIN
举例6:
#!/bin/bash
#将用户输入的字符转为大写,当输入q时退出
read -p “Input string: “ STRING
while [ $STRING != ‘q’ ];do
echo $STRING | tr ‘a-z’ ‘A-Z’
read -p “Input string again: “ STRING
done
举例7:
#!/bin/bash
#按步骤生成yum仓库文件
REPOFILE=/etc/yum.repos.d/$1
if [ -e $REPOFILE ];then
echo "$1 is exists."
exit 3
fi
read -p "Repository ID: " REPOID
until [ $REPOID == 'quit' ];do
echo "[$REPOID]" >> $REPOFILE
read -p "Repository Name: " REPONAME
echo "name=$REPONAME" >> $REPOFILE
read -p "Repository baseurl: " REPOURL
echo "baseurl=$REPOURL" >> $REPOFILE
echo -e "enabled=1\ngpgcheck=0" >> $REPOFILE
read -p "Repository ID: " REPOID
done
echo "Bye!"
举例8:
#!/bin/bash
#理解break
declare -i SUM=0
for I in {1..200};do
let SUM+=$I
if [ $SUM -gt 5000 ];then
break
fi
done
echo $I
echo $SUM
举例9:
#!/bin/bash
#理解continue
let SUM=0
let I=0
while [ $I -lt 100 ];do
let I++
if [ $[$I%2] -eq 0 ];then
continue
fi
let SUM+=$I
done
echo $SUM
举例10:
#!/bin/bash
#while特殊用法一
while :;do
read -p "File path: " FILEPATH
[$FILEPATH == 'quit' ] && break
if [ -e $FILEPATH ];then
echo "$FILEPATH exists."
else
echo "No $FILEPATH."
fi
done
#!/bin/bash
#while特殊用法二
useradd user1
useradd user2
useradd user3
useradd user4
useradd user5
let I=0
while read LINE;do
[`echo $LINE | awk -F: '{print $3}'` -gt 503 ] && continue
[`echo $LINE | awk -F: '{print $7}'` == '/bin/bash' ] && echo $LINE |awk -F: '{print $1}' && let I++
[$I -eq 5 ] && break
done < /etc/passwd
举例11:
#!/bin/bash
#检查一脚本是否有语法错误,若有错误按任意键(除q与Q)编辑,若无错则直接退出
until bash -n $1 &> /dev/null;do
read -p "Syntax error,[Q\q] to quit,other for editing: "CHOICE
case $CHOICE in
q|Q)
echo "Something wrong,quiting."
exit 5
;;
*)
vim + $1
esac
done
举例12:
#!/bin/bash
#理解函数
TWOSUM() {
echo $[$1+$2]
}
for I in {1..10};do
let J=$[$I+1]
TWOSUM $J $I
done
echo "$J plus $I is `TWOSUM $J$I`"
举例13:
#!/bin/bash
#函数应用:添加用户(存在显示failure,不存在则添加)
ADDUSER(){
USERNAME=$1
if ! id -u $USERNAME &>/dev/null;then
useradd $USERNAME
echo "$USERNAME" | passwd--stdin $USERNAME &> /dev/null
return 0
else
return 1
fi
}
for I in `seq 1 5`;do
ADDUSER user$I
if [ $? -eq 0 ];then
echo "add user$I is success."
else
echo "add user$I failure."
fi
done
举例14:
#!/bin/bash
#函数应用:查看指定网段是否畅通
PING() {
if ping -c 1 -w 1 $1 &> /dev/null;then
return 0
else
return 1
fi
}
for I in {150..160};do
PING 192.168.1.$I
if [ $? -eq 0 ];then
echo "192.168.1.$I is up."
else
echo "192.168.1.$I is down."
fi
done
举例15:
#!/bin/bash
#查看分区、内存、交换区使用情况。
cat << EOF
d)show disk usages.
m)show memory usages.
s)show swap usages.
q)quit.
EOF
read -p "Your choice: " CHOICE
until [ $CHOICE == 'q' ];do
case $CHOICE in
d)
echo "Disk Usages:"
df -hP
;;
m)
echo "Memory Usages:"
free -m | fgrep "Mem"
;;
s)
echo "Swap Usages:"
free -m | fgrep "Swap"
;;
*)
echo "Unknow!."
cat << EOF
d)show disk usages.
m)show memory usages.
s)show swap usages.
q)quit.
EOF
esac
read -p "Your choice again: " CHOICE
done
举例16:
#!/bin/bash
#函数应用:查看磁盘、内存、交换分区使用情况。
SHOW() {
cat << EOF
d)show disk usage.
m)show memory usage.
s)show swap usage.
q)show quit.
EOF
}
read -p "Your choice: " CHOICE
until [ $CHOICE == 'q' ];do
case $CHOICE in
d)
echo "Disk Usages:"
df -hP
;;
m)
echo "Memory Usages:"
free -m | fgrep "Mem"
;;
s)
echo "Swap Usages:"
free -m | fgrep "Swap"
;;
*)
echo “Unknow!”
SHOW
;;
esac
read -p "Your choice again: " CHOICE
done
举例17:
#!/bin/bash
#trap练习,信号捕捉及临时文件的创建删除
NET='192.168.101.'
FILE=`mktemp /tmp/file.XXXXX`
clearup() {
echo 'quit.'
rm -f $FILE
exit 1
}
trap 'clearup' INT
for I in {200..210};do
if ping -c 1 -W 1 $NET$I &> /dev/null;then
echo "$NET$I is up."
else
echo "$NET$I is down."
fi
done
举例18:
#!/bin/bash
#数组练习,找出已知数组中的最大数
declare -a ARRAY=(1 2 3 44 5 6 7 8 9 10)
declare -i MAX=${ARRAY[0]}
INDEX=$[${#ARRAY[*]}-1]
for I in `seq 0 $INDEX`;do
if[ $MAX -lt ${ARRAY[$I]} ];then
MAX=${ARRAY[$I]}
fi
done
echo $MAX
举例19:
#!/bin/bash
#数组练习,随机生成数组中各元素的值,找出最大值
for J in {0..9};do
ARRAY[$J]=$RANDOM
sleep 1
echo -n "${ARRAY[$J]} "
done
echo
declare -i MAX=${ARRAY[0]}
INDEX=$[${#ARRAY[*]}-1]
for I in `seq 0 $INDEX`;do
if[ $MAX -lt ${ARRAY[$I]} ];then
MAX=${ARRAY[$I]}
fi
done
echo $MAX
举例20:
#!/bin/bash
#自动生成空白脚本文件的头部;检查已存在脚本文件的语法
if ! grep "[^[:space:]]" $1&> /dev/null;then
cat > $1 << EOF
#!/bin/bash
# name: `basename $1`
# description:
# version: 0.0.1
# author: jowin
# datetime: `date "+%F %T"`
# Usage: `basename $1`
EOF
chmod +x $1
vim + $1
fi
until bash -n $1 &> /dev/null;do
read -p "syntax error:q|Q is quit,others is edit: " OPT
case $OPT in
q|Q)
echo "quit."
exit 4
;;
*)
vim + $1
;;
esac
done
-------------------------------------------------------------------------------------------------------------
举例21:
#!/bin/bash
#在上例基础上,使用选项为脚本头部添加描述信息,用法为./mkscript –d “STRING” SCRIPT_FILE
while getopts ":d:" OPT;do
case$OPT in
d)
DEC=$OPTARG
;;
*)
echo "Usage: mkscript [-d DESCRIPTION] FILE"
;;
esac
done
shift $[$OPTIND-1]
if ! grep "[^[:space:]]" $1&> /dev/null;then
cat > $1 << EOF
#!/bin/bash
# name: `basename $1`
# description: $DEC
# version: 0.0.1
# author: jowin
# datetime: `date "+%F %T"`
# Usage: `basename $1`
EOF
chmod +x $1
vim + $1
fi
until bash -n $1 &> /dev/null;do
read -p "syntax error:q|Q is quit,others is edit: " OPT
case $OPT in
q|Q)
echo"quit."
exit 4
;;
*)
vim + $1
;;
esac
done
-------------------------------------------------------------------------------------------------------------
举例22:
#export PATH=$PATH:/root
#mkscript -d “get interface ip” getinterface.sh(使用上例的脚本生成此脚本文件,编辑如下)
#!/bin/bash
# name: getinterface.sh
# description: get interface ip
# version: 0.0.1
# author: jowin
# datetime: 2015-10-27 19:23:13
# Usage: getinterface.sh
USAGE() {
echo "Usage: getinterface.sh [-i INTERFACE] [-I IP_ADDR]"
}
SHOWIP() {
if !ifconfig | grep -o "^[^[:space:]]\{1,\}" | grep $1 &>/dev/null;then
return 13;
fi
echo -n "${1}: "
ifconfig $1 | grep -o "inetaddr:[0-9\.]\{1,\}" | cut -d: -f2
}
SHOWETHER() {
if !ifconfig | grep -o "inet addr:[0-9\.]\{1,\}" | cut -d: -f2 | grep $1&> /dev/null;then
return 14;
fi
echo-n "${1}: "
ifconfig | grep -B 1 $1 | grep -o"^[^[:space:]]\{1,\}"
}
while getopts ":i:I:" OPT;do
case$OPT in
i)
SHOWIP $OPTARG
[$? -eq 13 ] && echo "wrong interface"
;;
I)
SHOWETHER $OPTARG
[$? -eq 14 ] && echo "wrong ip"
;;
*)
USAGE
;;
esac
done
-------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------
以上是学习《马哥运维课程》做的笔记。
一、1、基础正则表达式:
* 前一个字符匹配0次或任意多次
. 匹配除了换行符外任意一个字符
^ 匹配行首,例:^Hello,匹配以Hello开头的行
$ 匹配行尾,例:Hello$匹配以Hello结尾的行
[] 中括号中指定的一个字符,例:[0-9]、[a-z]
[^] 匹配中括号字符以外的任意一个字符,例:[^0-9]、[^a-z]
\ 转义符,取消特殊符号的含义
\{n\} 表示其前面的字符恰好出现n次,例:[0-9]\{4\}、手机号[1][3-8][0-9]\{9\}
\{n,\} 表示其前面的字符出现不小于n次,例:[0-9]\{2,\}表示2位以上的数字
\{n,m\} 表示其前面的字符至少出现n次,最多出现m次,例:[a-z]\{6,8\}表示6到8位小写字母
2、扩展正则表达式:#grep–E 或 egrep
+ 前一个字符匹配1次或任意多次
? 前一个字符出现0次或1次,例:colou?r匹配colour和color
| 匹配两个或多个分支选择,例:was|his,匹配包含was的行,也包含his的行
() 匹配其整体为一个字符,即模式单元,可理解为由多个单个字符组成的大字符,例:(dog)会出现dog,dogdog,dogdogdog等;hello(world|earth),匹配hello worlda或hello earth
注:grep、awk、sed等命令支持正则表达式;ls、find、cp支持shell通配符
3、#grep “a*” t.txt (匹配任何内容
#grep“aa*” t.txt (匹配至少包含一个a的行
#grep“aaa*” t.txt (匹配至少包含二个a 的行
#grep“s..d” t.txt (匹配s和d之间一定有2个字符的单词
#grep“s.*d” t.txt (匹配s和d之间有任意个字符
#grep“^M” t.txt (匹配以M开头的行
#grep“N$” t.txt (匹配以N结尾的行
#grep“^$” t.txt (匹配空白行
#grep“^….$” t.txt (匹配四个字符的行
#grep“[0-9]” t.txt (匹配含数字的行
#grep“^[a-z]” t.txt (字母开头的行
#grep“[^A-Z]” t.txt (非大写字母的行
#grep“\.$” t.txt (以点结尾的行
#grep“a\{3\}” t.txt (三个连续a的行
#grep“[0-9]\{3,\} t.txt (至少三个数字开头的行
#grep“[su]a\{3,\}[iI] t.txt (s或u和i或I之间至少有三个连续的a
#grep“sa\{1,3\}i” t.txt (s和i之间最少有1个a最多有3个a
二、字符截取和替换命令
1、#cut 选项 文件名
-f列号 fields提取第几列
-d“分隔符” delimiter指定分隔符
-c字符范围 通过字符范围进行字段提取,行首为0;“n-”表示从第n个字符到行尾;“n-m”表示从第n个字符到第m个字符;“-m”表示从第1个字符到第m个字符
例:#cut –f 2s.txt
#cut–f 2,3 s.txt
#cut–c 8- s.txt
#cut–d “:” –f 1,3 s.txt
2、awk编程
>#printf ’输出类型输出格式’ 输出内容
输出类型:
%ns 输出字符串,n指代输出几个字符
%ni 输出整数,n指代输出几个数字
%m.nf 输出浮点数,m代表总数位,n是小数位。例:%8.2f表示6位整数,2位小数
例:#printf ‘%s’$(cat s.txt)
#printf‘%s\t%s\t%s\t%s\t \n’ $(cat s.txt)
#printf‘%i\t%s\t%i\t%8.2f \n’ $(cat s.txt) | grep –v Name
-
awk ‘条件1{动作1} 条件2{动作2}……’ 文件名
动作:格式化输出,流程控制语句
注:在awk编程中因命令语句长,输入格式时需注意:
》多个条件{动作}可用空格分割也可用回车分割;
》在一个动作中,如果需要执行多个命令,要用分号分割或用回车分割;
》变量的赋值与调用都不用加$符号;
》条件判断两个值是否相同使用==,不要和变量赋值混淆。
例:#awk ‘{printf$2 “\t” $6 “\t” “\n” }’ s.txt (printf输出时在输出格式的最后要加“\n”,而print则不用。
#df–h | awk ‘{print $2 “\t” $6}’ s.txt
#cats.txt | grep –v Name | awk ‘$6 >= 87 {print $2}’ (加入条件后,只有条件成立动作才执行
#awk‘BEGIN {print “this is a transcript”} {print $2 “\t” $6}’ s.txt (awk只要检测不到完整的‘’就不会执行,所以不用加换行符;BEGIN在读取数据前仅执行一次
#awk‘END {print “The end!”} {print $2 “\t” $3}’ s.txt
#awk‘$2 ~ /sb/ {print $6}’ s.txt (第二个字段包含有sb则打印第六字段
#awk‘/sd/ {print}’ s.txt (打印sd的成绩
#df–h | awk ‘/sda[0-9]/ {print $1 “\t” $5}’
#cat/etc/passwd | grep “/bin/bash” | awk ‘BEGIN{FS=”:”} {print $1 “\t” $3}’
#cat/etc/passwd | grep “/bin/bash” | awk ‘BEGIN{FS=”:”} {print $1 “\t” $3 “\t 行号:”\
>NR “\t 字段数: “ NF}’ (NR总数据的第几行,NF总字段数
注:awk是列提取命令,但也要按行来读入,执行过程:
》如有BEGIN,则先执行BEGIN定义的动作;
》读入第一行,第一行的数据依次赋予$0,$1,$2等变量,$0代表此行的整体数据,$1代表第一个字段,$2代表第二个字段;
》依据条件判断动作是否执行,符合执行,否则读入下一行数据,若没条件,则每行都执行动作
》读入下一行数据,重复以下步骤。
#awk‘NR==2 {php1=$3}
NR==3{php2=$3}
NR==4{PHP=$3;total=php1+php2+php3;print”total php is ” total}’ s.txt
#awk‘NR>=2 {test=$4}
test>90{print $2 “is a good man!”}’ s.txt
function函数名 (参数列表){
函数体
}
#awk ‘functiontest(a,b) {print a “\t” b}
{test($2,$6)}’ s.txt
#vipass.awk
BEGIN‘{FS=”:”}
{print$1 “\t” $3}
#awk–f pass.awk /etc/passwd
3、sed(streameditor,对文本文件和标准输入进行编辑,键盘输入、文件重定向、字符串、变量、来自管道的文本
#sed选项 ‘动作’文件名
-n 有此项,输出时只会显示sed处理的行
-e 对输入数据应用多条sed命令编辑
-f脚本文件 与#awk –f类似
-r 支持正则表达式
-I 直接修改源文件中数据,而不是由屏幕输出
动作:
a\ append追加,在当前行后添加一行或多行,每行末尾用\代表数据未完,最后一行不用
c\ 行替换,用c后面的字符串替换原数据行,多行时用\代表数据未完,最后一行除外
i\ insert在当前行前插入一行或多行,多行时每行末尾用\代表数据未完,最后一行除外
d 删除指定的行
p 打印输出指定的行
s 字符替换,格式:行范围s/旧字串/新字串/g
行数据操作:例子:
#sed–n ‘2p’ s.txt
#sed‘2,4d’ s.txt
#sed‘2a hello’ s.txt
#sed‘2i hello \
>world’s.txt (行末用\代表数据未完,在第二行前插入两行数据
#sed–n ‘2i’ hello \
>world’s.txt (仅显示处理的行
#cats.txt |sed ‘2c No such person’ (将第二行替换
#sed–i ‘2c No such a person’ s.txt(直接处理文件数据,-i谨慎使用
字串替换:例子:
#sed‘s/旧/新/g’ s.txt
#sed‘3s/74/99/g’ s.txt
#sed‘4s/^/#/g’ s.txt
#sed–e ‘s/sb//g ; s/sd//g’ s.txt
三、字符处理命令
》#sort 选项 文件名
-f 忽略大小写ignore-case(foldlower case to upper case characters)
-b 忽略每行前面的空白部分ignoreleading blanks
-n 以数值排序numeric-sort
-r 反射排序reverse
-u 排除重复行,同uniqu命令
-t 指定分隔符,默认是制表符
-kn[,m] 按指定的字串范围,从n字段开始,m字段结束(默认到行尾)
例:#sort/etc/passwd
#sort–r /etc/passwd
#sort–t “:” –k 3,3 /etc/passwd (只用第3字段排序
#sort–n –t “:” –k 3,3 /etc/passwd
》#uniq 选项 文件名 (-i 忽略大小写
》#wc 选项 文件名 (-l,line;-w,word;-m字符数
四、条件判断
1、文件类型判断:
例:#[ -e /root/sh/ ]
#echo$?
#[ -d /root/sh ] && echo “yes”|| echo “no”
2、文件权限判断:
#[ -w s.txt ]&& echo “yes” ||echo “no”
3、两个文件之间进行比较:
#[ /root/s.txt -ef /tmp/sut.txt ] && echo “yes”|| echo "no” (判断是否硬链接
4、两个整数之间比较:
#[ 23 -ge 22 ]&& echo “yes” || echo “no”
5、字符串的判断:
#[ -z “$name” ] && echo “yes” || echo “no”
6、多重条件判断:
#[ -n “$aa” -a -n “$aa” -gt 23] && echo “yes” || echo “no”
五、流程控制:
1、if条件判断:
》单分支:
if[ 条件判断式 ];then
程序
fi
或
if[ 条件判断式 ]
then
程序
fi
注:使用fi结尾,和一般语言使用大括号不同;条件判断式使用test命令判断,中括号和条件判断式之间必须有空格;then扣是符合条件之后执行的程序,then可放在[]之后用分号分隔,也可换行写入不写分号
例:#vi fi1.sh
#!/bin/bash
rate=$(df–h | grep “/dev/sda3” | awk ‘{print $5}’ | cut –d “%” –f 1)
if[ “$rate” -ge 80 ]
then
echo“Warning! /dev/sda3 is full!”
fi
》双分支
if [ 条件判断式 ]
then
条件成立时执行的程序
else
条件不成立时执行的程序
fi
例:#vi backmysql.sh
#!/bin/bash
ntpdateasia.pool.ntp.org &>/dev/null
date=$(date+%Y%m%d)
size=$(du–sh /var/lib/mysql)
if[ -d /tmp/dbbak ]
then
echo“Date: $date!” > /tmp/dbbak/dbinfo.txt
cd/tmp/dbbak
tar–zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &>/dev/null
rm–rf /tmp/dbbak/dbinfo.txt
else
mkdir/tmp/dbbak
echo“Date: $date!” > /tmp/dbbak/dbinfo.txt
echo“Date size: $size” >> /tmp/dbbak/dbinfo.txt
cd /tmp/dbbak
tar–zcf mysql-lib-$date.tar/gz /var/lib/mysql &>/dev/null
rm–rf /tmp/dbbak/dbinfo.txt
fi
例:vi autostart.sh
#!/bin/bash
port=$(nmap–sT 192.168.4.210 | grep tcp | grep http| awk ‘{print $2}’)
if [ “$port” == “open” ]
then
echo“$(date) httpd is OK!” >> /tmp/autostart-acc.log
else
/etc/rc.d/init.d/httpdstop &>/dev/null
/etc/rc.d/init.d/httpdstart &>/dev/null
echo“$(date) restart httpd!” >> /tmp/autostart-err.log
fi
》多分支
if [ 条件判断式 ]
then
当条件判断式1成立时,执行程序1
elif [ 条件判断式 ]
then
当条件判断式2成立时,执行程序2
……
else
当所有条件都不成立时,执行此程序
fi
例:#vi if-elif.sh
#!/bin/bash
read–p “please input a filename: “ file
if [ -z “$file” ]
then
echo“Error,please input a filename.”
elif [ ! –e “$file” ]
then
echo“Your input is not a file”
elif [ -f “$file” ]
then
echo“$file is a regulaar file.”
elif [ -d “$file” ]
then
echo“$file is a directory!”
else
echo“$file is an other file!”
fi
2、多分去case条件语句:case只判断一种条件关系,而if可判断多种条件关系。
case$变量名 in
“值1”)
如果变量的值等于值1,则执行程序1
;;
“值2”)
如果变量的值等于值2,则执行程序2
;;
……
*)
如果变量的值都不是以上的值,执行此程序
;;
esac
注:case语句会取出变量中的值,与语句体中的值逐一比较,符合则执行相应程序,不符依次比较下一个值。若所有值都不符合,则执行*)后的程序,*代表所有其它值;以case开头,esac结尾;每个分支之后要用;;结尾,代表该程序段结束。
例:#vi case.sh
#!/bin/bash
read–p “please chose yes/no: “ cho
case$cho in
“yes”)
echo“Your choose is yes!”
;;
“no”)
echo“Your choose is no”
;;
*)
echo“Your choose is error!”
;;
esac
3、for循环是固定循环,在循环时已知道要循环几次,也称计数循环
for 变量 in 值1 值2 值3
do
程序
done
注:in后有几个值就循环几次,每次循环都把值赋予变量
for ((初始值;循环控制条件;变量变化))
do
程序
done
注:初始值,在循环开始时需要给变量赋予初始值,如i=1;循环控制条件,用于指定循环的次数,如i<=100;每次循环之后,变量该如何变化,如i=i+1,每次循环之后变量都加1.
例:vi auto-tar.sh
#!/bin/bash
cd /bin/bash
cd /lamp
ls *.tar.gz > ls.log
for i in $(cat ls.log)
do
tar -zxf $i &>/dev/null
done
rm–rf /lamp/ls.log
例:#vi useradd.sh
#!/bin/bash
read -t 30 -p “Please input username: “ name
read -t 30 -p “please input the number of users: “ num
read -t 30 -p “please input the password: “ pass
if [ -n “$name” –a -n “$num” -a -n “$pass” ]
then
y=$(echo $num | sed ‘s/[0-9]//g’)
if [ -z “$y” ]
then
for ((i=1;i<=$num;i=i+1))
do
/usr/sbin/useradd $name$i &>/dev/null
echo $pass | /usr/bin/passwd --stdin $name$i &>/dev/null
done
fi
fi
4、while循环:只要条件判断式成立,循环就一直继续,直到条件不成立才停止。
while[ 条件判断式 ]
do
程序
done
5、until循环:条件判断式不成立则进行循环,条件成立,则中止循环。
until[ 条件判断式 ]
do
程序
done
6、函数:
function 函数名 () {
程序
}
7、特殊流程控制语句:
》exit语句:在脚本中是退出当前脚本,#exit 返回值,返回值可自己定义,用$?查询,若没定义返回值,则是执行exit语句之前最后执行的命令的返回值。
》break语句会结束整个当前循环,而continue语句结束单次当前循环,下次循环会继续
for ((条件判断语句))
do
程序语句
break(continue)
程序语句
done
例:#vi ping.sh
#/bin/bash
echo “这个脚本是统计/root/ip.txt所有ip地址的丢包率”
for i $(cat /root/ip.txt)
do
aa=$(ping -c 3 $i | grep ttl | wc -l)
bb=$(( 3 - $aa ))
cc=$(echo “$bb 3” | awk ‘{ print “%5.2f% \n”,$1/$2*100}’)
echo “$i的丢包率是:$cc”
done
本文转自 chaijowin 51CTO博客,原文链接:http://blog.51cto.com/jowin/1651732,如需转载请自行联系原作者