Linux阶段总结shell脚本

shell脚本知识储备汇总




语言类型


强类型:定义变量必须指定类型;


参与的运算必须要符合类型要求(字符串不能和数值相加等);


调用未声明变量会报错


弱类型:定义变量无需指定类型;


默认为字符型参与运算时会自动进行类型转换;


变量无需事先定义也可调用(空)


解释器:


shell是解释器bash是可用的具体解释器(shell是车这个概念bash是宝马车)


bash  sh ksh(需自己安装) tcsh csh等



bash的基本特性:快捷键;Tab补齐命令和路径;history;命令别名;标准输入输出;重定向;管道;


Redhat6选项不可Tab 需要装bash-completion这个包才能Tab





更改用户shell环境(解释器):   useradd -s 创建用户时


usermod -s 改已存在用户


vim /etc/passwd 改配置


history:查看条数: grep HISTSIZE /etc/profile


    echo $HISTSIZE


    history | wc -l


      history | tail


调用:     !1028


    !cat


清空(两条命令必须同时执行): history -c 


    > ~/.bash_history  空重定向到HISTFILE文件



别名alias: grep '^alias' ~/.bashrc 家目录下的隐藏文件


unalias 取消



重定向:


(正确覆盖输出);


>> (正确追加输出);


2> (错误覆盖输出);


2>> (错误覆盖输出);


&>       (正确错误覆盖输出);


&>>  (正确错误追加输出);


>&2      把正确变成错误输出


<; (导入文件)


tips: 写脚本可以上来就date >(>>) xx.log  再在里面把命令执行错误的信息2>(>>) xx.log以执行时间


进行区分了方便查看某次执行时的错误



非交互式写邮件三种:


mail -s biaoti user <<EOF


XX


YY


EOF


echo haha | mail -s biaoti user  管道


mail -s biaoti user < mail.txt 导入写好文件



脚本的执行方式:


/root/first.sh   ./first.sh 绝对路径(相对)执行 需+x


bash、sh /root/first.sh 解释器执行(相当于作为参数)不需+x  开了子进程pstree可以验证


source、./root/first.sh 解释器执行(相当于作为参数) 不需+x  当前进程执行


(用于使改的配置马上生效而不重启,相当于重读配置文件。



shell变量:


变量的类型


储值类型:


数值型  :整数型;浮点型;复数。。。


字符型  :


生效范围:


本地变量:当前Shell环境中有效,而在子Shell环境中无法直接使用


局部变量:


全局变量:



使用类型:


自定义变量: a=1 echo ${a}10  110 本地变量


TIPS:查看变量时,若变量名称与后面要输出的字符串连在一起,则应该以{}将变量名括起来以便区分


环境变量:  PWD、USER、HOME、SHELL、HISTSIZE、HISTFILE、PATH、PS1一级提示符、


PS2二级提示符、LOGNAME登录名 RANDOM HOSTNAME TERM记录终端类型


环境变量相关文件:(注意开机时读取文件顺序)


全局文件为/etc/profile,对所有用户有效


用户文件为~/.bash_profile,仅对指定的用户有效


env可查看所有环境变量:


set可查看所有变量



位置变量: $1、$2、$10、……


预定义变量: $0、$$、$?、$#、$*、$@


#!/bin/bash


echo $0                                        //脚本的名称


echo $1                                        //第一个参数


echo $2                                        //第二个参数


echo $*                                        //所有参数 当成一个整体字符串


echo $@           //所有参数 每个参数为独立字符串


echo $#                                        //所有的综合


echo $$                                        //当前进程的进程号


echo $?                                        //上一个命令执行状态返回值


shift 3 //换岗 踢掉参数(默认为一个)



使用read命令从键盘读取变量值


read -p "请输入。。:" i  用户输入的值赋予变量i


将回显功能关闭(stty -echo)  输password时


将回显功能恢复(stty echo)   输完password时


 


使用export发布全局变量


export PATH="$PATH:/usr/local/haha/bin"临时加了个可执行路径。


export XX="1234" 



变量的定义: 名称只能为字母数字下划线且只能以数字和下划线开头


a=123 字符串


a=$b 引用其他变量的值为自己的值


a=$(date) 命令结果引用为自己的值



三种引号对赋值的影响:


强引用: 单引号  界定一个完整字符串


弱引用: 双引号 界定一个完整的字符串且屏蔽特殊符号


命令结果引用:  反撇号或$() 引用命令执行的结果赋予变量



变量的调用


调用变量时,若变量名称与后面输出的字符串连在一起,以{}将变量名括起来以便区分


shell的数值运算


整数运算:


expr \*


$[]或$(()) 运算符两侧可以无空格 引用变量可省略 $


let 不显示结果


expr或$[]、$(())方式只进行运算,并不会改变变量的值;而let命令可以直接对变量值做运算再保存新的值


let X++ X-- X+=7 X-=7 X*=7 X/=7 X%=7  强大!!


小数运算:


bc scale=N 小数位的长度


echo 'scale=4;12.34-5.678' | bc 


6.662  虽然小数位长为4但运算数值最长为3 也只显示3位。


tips:bc支持条件测试正确返回1错误返回0 与$?相反


条件测试


“test 表达式”或者[ 表达式 ]都可以,表达式两边至少要留一个空格。注意空格空格空格!


[  $USER == "root"  ] 等号两边有空格(没有就相当字符串a) 表达式和[]间有空格!!!


字符串匹配


== != -z 检查变量的值是否为空    -n或!-z 检查变量的值是否非空



比较整数值的大小


eq


ne


gt


ge


lt


le


识别文件/目录的状态


-e


-d


-f


-r


-w


-x


多个条件/操作的逻辑组合


&&,逻辑与 第一个为假后面都不用执行


||,逻辑或 第一个为真后面都不用执行


!,逻辑非


短路运算:[ $X -gt 10 ] && echo "YES" || echo "NO"


  第一个为真必须yes而不会No了,因为前面整体为真了||运算已经结束。


if选择结构


单分支就直接: [ $X -gt 10 ] && echo "YES"


双分支: [ $X -gt 10 ] && echo "YES" || echo "NO"


if  [ 条件测试 ];then  


命令序列1


        else    命令序列2


fi


多分支:


if  [ 条件测试 ];then  


命令序列1 执行完就exit


elif


命令序列2 执行完就exit


        else   


命令序列3 执行完就exit


fi


案例:


检测/media/cdrom目录,若不存在则创建


检测并判断指定的主机是否可ping通


ping -c 3 -i 0.2 -W 3 192.168.4.5 


-c 3(3次)-i0.2间隔0.2秒-W 3反馈的超时秒数3


从键盘读取一个论坛积分,判断论坛用户等级



被阉割的选择结构case


case分支属于匹配执行的方式,它针对指定的变量预先设置一个可能的取值,判断该变量的实际取值是否与 预设的某一个值相匹配,如果匹配上了,就执行相应的一组操作,如果没有任何值能够匹配,就执行预先设 置的默认操作。


case  变量值  in


模式1)


    命令序列1 ;;


模式2)


    命令序列2 ;; 注意格式!!!必须是双分号。。


    .. ..


*)


    默认命令序列


esac


#!/bin/bash


case $1 in


redhat) //可以换成-n-i来代表选项做一个功能强大的命令出来


        echo "fedora";;


fedora)


        echo "redhat";;


    *)                                              //默认输出脚本用法


    echo "用法: $0 {redhat|fedora}"


esac






循环结构


for循环


for  变量名  in  值列表或 {1..5} 或 `seq 5` 或((i=1;i<=5;i++))


do


    命令序列


done




几个循环造数工具


{1..n}


seq 5(1开始到5) seq 2 10(2开始到10)seq 2 2 10(2开始中间跳2;即偶数)seq 1 2 10(奇数)


[$RANDOM%10+1] 随机产生1-10的数


((i=1;i<=5;i++))  初值1;条件<=5;步长为1


案例:


利用for循环来检测多个主机的存活状态



while循环


while  [条件测试]


do


    命令序列


done



死循环一般格式 while :


do


        命令序列


done


通常会写成死循环加退出的格式



while :


do


if[条件判断];then


        命令序列 && break


done


案例:  提示用户猜测一个随机数,直到才对为止


检测192.168.4.0/24网段,列出不在线的主机地址




shell函数


将一些需重复使用的操作,定义为公共的语句块,即可称为函数。通过使用函数,可以使脚本代码更加简洁,增强易 读性,提高Shell脚本的执行效率


相当于一段代码(一串命令)取了一个名字类似定义了alias


定义错了直接重新定义会覆盖,类似a=1;a=2后会覆盖前。




function  函数名 {


    命令序列


    .. ..


}




函数名() {


    命令序列


    .. ..


}


mycd(){                        //定义函数


> mkdir $1


> cd $1


> }


案例:颜色输出的命令:echo -e "\033[32;41mOK\033[0m"。3X为字体颜色分号隔开4X为背景颜色。


#!/bin/bash


cecho() {


    echo –e "\033[$1m$2\033[0m"


}


cecho 32 OK


cecho 33 OK


cecho 34 OK


cecho 35 OK









中断和退出


break可以结束整个循环;


continue结束本次循环,进入下一次循环;


exit结束整个脚本、


#!/bin/bash


for  i   in   {1..5}


do


        [  $i  -eq  3 ]&& break 


//这里将break替换为continue,exit分别测试脚本执行效果       


echo $i


done


echo  Over


break 12Over  continue1245Over  exit12


案例:求输入数的和输0退出


#!/bin/bash


while  read  -p  "请输入待累加的整数(0表示结束):"     x


do


    [ $x -eq 0 ]  &&  break


    SUM=$[SUM+x]


done


echo "总和是:$SUM"


案例:求1-20内所有数的平方跳过非6的倍数




#!/bin/bash


i=0


while  [ $i -le 20 ]


do


    let i++


    [ $[i%6] -ne 0 ]  &&  continue


    echo $[i*i]


done


exit 2



文本处理


字符串截取及切割


echo $变量名 | cut -b 起始位置-结束位置


echo $phone | cut -b 1-6 1-6


echo $phone | cut -b 8-    8到最后


echo $phone | cut -b 9 第9个


echo $phone | cut -b 3,5,8 第3 5 8个


expr substr "$变量名" 起始位置 长度


${变量名:起始位置:长度} 截取 (索引位置) ${phone:0:6} ${phone::6}


${变量名/old/new} 替换第一个


${变量名//old/new} 替换全部


${变量名#*关键词} (关键词也删)左向右 最短匹配删除 ${A#*:} 从左侧到第一个:的所有


${变量名##*关键词} 左向右 最长匹配删除 ${A##*:}从左侧到最后一个:的所有


${变量名%关键词*} 右向左 最短匹配删除 ${A%:*} 从右侧到第一个:的所有


${变量名%%关键词*} 右向左 最长匹配删除 ${A##*:}从右侧到最后一个:的所有


${变量名:-初值} 有初值就用初值 无初值就用赋予的初值


${XX:-123}xx无初值就赋予123为初值



随机密码 #!/bin/bash


x=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789


//所有密码的可能性是26+26+10=62(0-61是62个数字)


for i in {1..8}


do


num=$[RANDOM%62]


tmp=${x:num:1}


pass=${pass}$tmp 字符串相加


done


echo $pass


改扩展名 touch {a,b,c,d,e,f,g,h,i}.doc


#!/bin/bash


for FILE in `ls *.doc($1)`


do


    mv $FILE  ${FILE%.*}.txt("$2") (${FILE/doc/txt})  $1换成$2的扩展名


done



初值的处理 #!/bin/bash


read -p "请输入一个正整数:" x


x=${x:-1}


i=1; SUM=0


while [ $i -le $x ]


do


    let SUM+=i


    let i++


done


echo "从1到$x的总和是:$SUM"




#!/bin/bash


read  -p   "请输入用户名:"   user


read  -p   "请输入用户名:"   pass


[ -z $user ] && exit                    //如果无用户名,则脚本退出


pass=${pass:-123456}                    //如果用户没有输入密码,则默认密码为123456


useradd  $user


echo "$pass"  | passwd   --stdin   $pass




expect预期交互 :模拟人机交互过程(要提前对交互过程非常熟悉)



install  expect


#!/bin/bash


for i in 10 11 {1..245} seq 1 254


do


expect << EOF


spawn ssh -o StrictHostKeyChecking=no 172.25.0.$i   #//创建交互式进程   


expect "password:" { send "123456\r" }              #//自动发送密码


expect "#   { send "pwd > /tmp/$user.txt \r" }      #//发送命令


expect "#"  { send "exit\r" }


EOF


done


expect脚本的最后一行默认不执行


如果不希望ssh时出现yes/no的提示,远程时使用如下选项:


# ssh -o StrictHostKeyChecking=no server0


shell数组


整体赋值:a=(haha xixi hehe ) a[0]=haha 0是索引值为0的量


单个赋值:a[0]=haha  


  a[1]=xixi


  a[2]=hehe




文本处理三剑客grep 


三剑客都用''单引号!


正则表达式



字符匹配(单个字符).点 任意单个字符


[] [xyz] 集合,里面的任意单个字符


[a-z] [:lower:]


[A-Z] [:upper:]


[a-Z] [:alpha:]


[0-9] [:digit:]


[0-9a-Z][:alnum:]


[:space:]


[^] [^xyz] 集合取反


[^a-z]


[^A-Z]


[^a-Z]


[^0-9]


[^0-9a-Z]


|(扩展)x|y 或者


匹配次数 * 前一个字符出现任意次


\{n,m\} 前一字符出现n到m次


\{n} 前一字符出现n次


\{n,\} 前一字符至少出现n次


\(\) 保留(复制)后向引用





次数匹配(扩展) 至少出现1次


出现0次或1次


{n}  出现n次


{n,} 至少出现n次


{n,m} 出现n到m次


{0,m} 至多出现m次








位置锚定 ^ 匹配行首(以什么开头)


$ 匹配行尾(以什么结尾)


\> 词尾


\< 词首


\b(扩展) 单词边界


^$


^[[:space:]]*$ 



后向引用 () 组合为整体,保留(复制)


分组(复制) ( )复制  \1 \2 \3 粘贴




\B 匹配非单词边界“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。



? 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的


非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串


例如,对于字符串“oooo”,“o+”将尽可能多地匹配“o”,得到结果[“oooo”]


而“o+?”将尽可能少地匹配“o”,得到结果 ['o', 'o', 'o', 'o']




正则表达式过滤案例:





egrep -c '/sbin/nologin$' /etc/passwd = egrep '/sbin/nologin$' /etc/passwd | wc -l  内置了统计功能


egrep -vc '/sbin/nologin$' /etc/passwd  


egrep -cv '.' /etc/rc.local 统计空行


egrep -c '^$' /etc/rc.local 统计空行




egrep -c ".*" /etc/httpd/conf/httpd.conf   //总行数




egrep -c "#" /etc/httpd/conf/httpd.conf   //含注释的行数


egrep -c -v '#|^$'  /etc/httpd/conf/httpd.conf 有效配置行(多数配置文件适用)


egrep -v '#|^$'  /etc/httpd/conf/httpd.conf > httpd.conf.min 保存有效配置行


egrep  '^ .+|^[^#]' test5-19-1.sh  脚本里真正的有效配置的行?具体情况具体分析啊



egrep -m 2 '/sbin/nologin$' /etc/passwd 只显示匹配到的前2行


  egrep '.*' /etc/rc.local 匹配所有


egrep 'exec(ution)*' /etc/rc.local 包含exec 和execution的行


ifconfig | egrep '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}' 包含MAC地址


MAC03="20:69:74:R2:C5:27"  




判断mac地址是否有效


echo $MAC03 | egrep -q '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}' && echo "有效" || echo "无效"



过滤有效的邮箱地址


egrep '[0-9a-zA-Z_.]{3,}@[0-9a-zA-Z.-]{2,}(\.[0-9a-zA-Z-]{2,})+' mail.txt 




sort -t: -k3 -n /etc/passwd |tail -1 | cut -d: -f3 UID最大的用户名


精确匹配root用户(3种方式)


egrep '^\broot\b' /etc/passwd


egrep '^\<root\>' /etc/passwd  


awk -F: '$1=="root"' /etc/passwd




如果root存在显示它的解释器


id root &> /dev/null && egrep '^\broot\b' /etc/passwd | cut -d: -f7


id root &> /dev/null && awk -F: '$1=="root"{print $NF}' /etc/passwd




egrep '\b[0-9]{2,3}\b' /etc/passwd  2-3位数字


某个脚本的有效语句:


egrep  '^[^#]' test5-19-1.sh | egrep '[^[:space:]]+.*#.*' 这个好恼火啊


egrep  '^[^#]|[^[:space:]]+.*#.*' test5-19-1.sh 




用户名同shell名的:


egrep '(^\b[a-Z0-9]+\b).*\1$' /etc/passwd


sync:x:5:0:sync:/sbin:/bin/sync


shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown


halt:x:7:0:halt:/sbin:/sbin/halt


bash:x:1005:1025::/home/bash:/bin/bash


nologin:x:1008:1028::/home/nologin:/sbin/nologin


案例:传递两个文件作为参数给脚本计算这两个文件所有空白行之和


k1=$(egrep "^[[:space:]]*$" $1 | wc -l


k2=$(egrep "^[[:space:]]*$" $2 | wc -l


echo "$[k1+k2]"


root centos user1用户的默认shell和uid


/etc/grub2.cfg文件中某单词后面跟一个小括号的行


使用echo输出一个绝对路径egrep找出其基名和目录名


ifconfig结果中1-255之间的数值(大量用|或者)


ifconfig结果中的IP地址




文本处理三剑客sed:



逐行处理软件 读一行处理一行 逐行处理读一行处理一行。


sed [选项] '条件指令' 文件


选项:


-n 屏蔽默认输出


-r 支持扩展正则


-i 修改源文件


条件:


行号 4    4;5  4,5   4,+10  


4~2(第4行和后面步长为2的行)


sed -n '2~2P' sed.txt  偶数行


sed -n '1~2P' sed.txt  奇数行


sed  -n '$=' a.txt            //输出文件的行数




/正则/


sed  -n 'p' a.txt            //输出所有行,等同于cat a.txt


sed  -n '4p' a.txt            //输出第4行


sed  -n '4,7p' a.txt        //输出第4~7行


sed  -n '4,+10p' a.txt        //输出第4行及其后的10行内容


sed  -n '/^bin/p' a.txt        //输出以bin开头的行




sed  '/xml/!d' a.txt         //删除不包含xml的行,!符号表示取反


sed  '$d' a.txt                //删除文件的最后一行


sed  '/^$/d' a.txt             //删除所有空行


tips: sed 's/xml//g'     a.txt     //将所有的xml都删除(替换为空串)






sed -n 's/2017/xxxx/gp' sed.txt //替换所有并打印


sed 's/xml/XML/'  a.txt        //将每行中第一个xml替换为XML


sed 's/xml/XML/3' a.txt     //将每行中的第3个xml替换为XML


sed 's/xml/XML/g' a.txt     //将所有的xml都替换为XML


sed 's/xml//g'     a.txt     //将所有的xml都删除(替换为空串)




sed 's#/bin/bash#/sbin/sh#' a.txt  //将/bin/bash替换为/sbin/sh 


(涉及到路径时用其他符合分隔)




sed '4,7s/^/#/'   a.txt         //将第4~7行注释掉(行首加#号)


sed 's/^#an/an/'  a.txt         //解除以#an开头的行的注释(去除行首的#号)


sed -n 's/.//2;s/.$//p' sed.txt 删除文件中每行的第二个、最后一个字


互换:(后向引用)


sed -rn 's/^(.)(.*)(.)$/\3\2\1/p' sed.txt 第一个、倒数第1个字符互换


sed -rn 's/^(.)(.)(.*)(.)(.)$/\1\4\3\2\5/p' sed.txt  第二第四互换




sed 's/[0-9]//' nssw.txt 删除文件中所有的数字


sed -r 's/[0-9]//g;s/^( )+//' nssw2.txt 删除所有数字、行首空格




sed -rn 's/([A-Z])/[\1]/gp' nssw.txt 为文件中每个大写字母添加括号


sed -rn 's/([a-Z0-9])/[\1]/gp' sed.txt  为文件中每个字母和数字添加括号


sed -rn 's/([a-Z0-9 ])/(\1)/gp' sed.txt 为文件中每个字母和数字空格添加括号





指令:


p 打印


d 删除


s 替换s/旧/新/g


a 指定行之后追加  


sed  '2a XX'   a.txt            //在第二行后面,追加XX


i 指定行之前插入


sed  '2i XX'   a.txt            //在第二行前面,插入XX


c 替换行


sed  '2c XX'   a.txt            //将第二行替换为XX


sed  '1c mysvr.tarena.com' /etc/hostname 修改主机名


脚本中修改配置文件时:


cp /etc/vsftpd/vsftpd.conf{,.bak}  给配置文件备份



/etc/hosts 这个文件非常有用:优先级比dns高 本地解析库




sed  -i  '$a 192.168.4.20  D  '  /etc/hosts


这一就可以ssh D 直接远程这个ip了。


sed  -i  '$a 192.168.4.20  www.baidu.com '  /etc/hosts


这样访问百度就快些了不用去找dns解析了 


文本处理三剑客awk:



awk [选项] '[条件]{指令}' 文件


多条编辑指令,可用分号分隔


默认将空格、制表符等作为分隔符


(cut sort 也是是默认空格Tab)


grep sed 不能直接打印某列要结合字符串的截取工具使用



-F 可指定分隔符  识别多种单个的字符


awk -F [:/] '{print $1,$10}' /etc/passwd



awk常用内置变量:




$0 文本当前行的全部内容




$1 文本的第1列




$3 文件的第3列,依此类推




NR 文件当前行的行号




NF 文件当前行的列数(有几列



awk -F: '{print NR,NF}' passwd.txt


awk -F: '{print NR,$NF}' /etc/passwd  永远打印最后一行!


awk -F: '{print NR,$NR}' /etc/passwd  第几行的时候就打印第几列!


可以打印常量:



awk -F: '{print $1,"的解释器:",$7}' /etc/passwd


打印常量必须要加双引号, 加逗号有空格,不加逗号无空格!



案例:


free |awk '/Mem/{print $NF}' 查看目前可用内存


ifconfig eth0 | awk '/RX p/{print $5}'  查看接收流量!


ifconfig eth0 | awk '/TX p/{print $5}'  查看发送流量!


df -h | awk '/\/$/{print $4}' #:\/$代表对/转义!!看跟分区可用空间。


df  | awk '/\/$/{print $4}'  故意不加单位 在脚本中好当成数值比较!!!


df -h / | tail -1 | awk '{print $4}' 第一个/ 代表要看/(根)的情况 提取最后一行 给awk



在网页中查看脚本:远程监控服务器网卡 内存等等情况(相当于动态页面了)


rpm -qa | grep httpd


cp /root/share/test5-19-1.sh /var/www/cgi-bin/test5-19-1.html 必须放在脚本专用目录下


chmod +x /var/www/cgi-bin/test5-19-1.html   必须要加X不然访问不了要报404. 看到404就检查有没有X


systemctl stop firewalld


setenforce 0 


ip/cgi-bin/test5-19-1.html   访问验证!!!


tips:


要想使访问时换行显示,要在/var/www/cgi-bin/test5-19-1.html 给这个文件加<br>然后再重启服务!!


而不是改之前的脚本文件,思路一定要清晰啊小伙子。


tips:


如果cp -P test5-19-1.html /var/www/html/


http://192.168.4.20/test5-19-1.html


访问到的就不是脚本执行的结果了,就是html 代码执行的结果了。如下


#!/bin/bash echo "Content-type: text/html" echo "" ifconfig eth0 | awk '/netmask/{print "Ip:"$2}' echo "


" ifconfig eth0 | awk '/RX p/{print "接收流量:"$5}' echo "


" ifconfig eth0 | awk '/TX p/{print "发送流量:"$5}' echo "


" df | awk '/\/$/{print "根分区可用:"$4}' echo "


" free |awk '/Mem/{print "内存可用:"$NF}'


路径一定要是/var/www/cgi-bin/test5-19-1.html 才能访问到脚步执行结果。(在未修改配置文件前提下)




格式化输出:



awk处理的时机:



处理第一行之前做准备工作 只做预处理的时候,可以没有操作文件


awk 'BEGIN{print x+1}'           #x可以不定义,直接用,默认值位0


中间进行逐行处理




处理完最后一行做总结




awk  [选项]  ' BEGIN{指令} {指令} END{指令}'  文件


BEGIN{ } 行前处理,读取文件内容前执行,指令执行1次


{ } 逐行处理,读取文件过程中执行,指令执行n次


END{ } 行后处理,读取文件结束后执行,指令执行1次


统计系统中使用bash作为登录Shell的用户总个数


awk 'BEGIN{x=0}/bash$/{x++} END{print x}' /etc/passwd


awk '/bash$/{x++} END{print x}' /etc/passwd  与上条命令等效 x未定义默认为0






格式化输出/etc/passwd


awk -F: 'BEGIN{print "User\tUID\tHome"} \


                                {print $1 "\t"  $3  "\t"  $6}     \ 使用“\t”显示Tab制表位


                          END{print "Total",NR,"lines."}' /etc/passwd


awk -F: 'BEGIN{print "用户名   家目录   UID"} {print $1,$6,$3 x++} END{print "总共"x"行"}' /etc/passwd


awk -F: 'BEGIN{print "用户名","家目录","UID"} {print $1,$6,$3} END{print "总共"NR"行"}' /etc/passwd


awk -F: 'BEGIN{print "用户名","家目录","UID"} {print $1,$6,$3 x++} END{print "总用户量:"x }' /etc/passwd


END{print "总共"NR"行"}  END{print "总共"x"行"}  打印最后的时候:注意变量不要引号,常量要引号,这是个坑啊!!!


awk -F: 'BEGIN{print "用户名\t家目录\tUID"} {print $1"\t"$6"\t"$3 x++}END{print "总用户量:"x }' /etc/passwd | column -t


column -t 自动排版对齐!!!awk -F: '{print $1,$2,$3 }' /etc/passwd | column -t




awk处理条件:


正则


awk -F: '/bash$/{print}' /etc/passwd


awk -F: '$1~/(zhangsan|root)/{print $7}' /etc/passwd  (支持扩展的正则)


~代表匹配:$1匹配后面的正则。并且是包含就算,模糊匹配 #第一列包含root或zhangsan的打印第7列。


[root@D share]# awk -F: '$1~/^root/' /etc/passwd


root:x:0:0:root:/root:/bin/bash


roota:x:1004:1024::/home/roota:/bin/bash 模糊匹配


awk -F: '$7!~/nologin$/{print $1,$7}' /etc/passwd    !~不匹配


使用数值/字符串比较设置条件




比较符号:== != > >= < <=


awk -F: '$1=="root"' /etc/passwd   常量要引号  精确匹配用户是为root的 不包含aroot root arootb 等等。


awk -F: 'NR==3'   打印第三行 不写{指令}默认为打印整行(所有列)


awk -F: '$3>=1000' /etc/passwd  大于等于1000的都是普通用户。


awk -F: '$3>10  && $3<5'  /etc/passwd  


tips:带逻辑判断的 要写合理  不然的话不会报错但是脚本执行结果不对,就不好排错了。


seq 100 | awk '$1%6!=0'  不能被6整除的。


逻辑测试条件



seq 100 | awk '$1%6==0 && $1%5==0'  5和6的公倍数。


seq 100 | awk '$1%7==0 || $1~/7/' 7整除或者包含7


awk '/Failed/ && /invalid/' /var/log/secure  


数学运算


awk 'BEGIN{a++;print a}'  1


case:


[root@svr5 ~]# cat getupwd-awk.sh


#/bin/bash


A=$(awk -F:  '/bash$/{print $1}' /etc/passwd)        ## 提取符合条件的账号记录


for i in $A


do


grep $i /etc/shadow | awk -F: '{print $1,"-->",$2}'                


done



awk 命令内部不能直接调用shell中的外部变量 $1==$i 要用-v选项来重新定义调用 很麻烦。


awk流程控制(if,for,while)



   if判断:


单分支:if(判断){命令} #第一个{表示命令开始    第二个}代表命令结束


多分支:if(判断){命令}else{命令} #所有指令都要放在{}里!!!!


awk -F: '{if($3<=1000) {i++}} END{print i} ' /etc/passwd   #结构很重要哈!!不行就先把结构框架打出来再往里面添加数据!!


awk -F: '{if($3<=1000){x++}else{y++}} END{print x,y}' /etc/passwd  






   for循环:




awk数组:


awk 'BEGIN{a++;print a}'  1  未定义的变量对它++就是1!!!!a=a+1 a未出现过,为空 空+1=1


awk 'BEGIN{a[0]++;print a[0]}'


非常重要:awk 'BEGIN{a[0]=00;a[1]=11;a[2]=22; for(i in a){print i,a[i]}}' 


0 0


1 11


2 22结果证明:i取值不是a的值而是a的下标。a[i]才是数组a的值。


awk数组的下标除了可以使用数字,也可以使用字符串,字符串需要使用双引号:


awk 'BEGIN{a["192.168.4.254"]=11;print a["192.168.4.254"]}'  11


awk 'BEGIN{a[192.168.4.254]=11;print a[192.168.4.254]}'  11


案例:        


背景:


awk 统计每一个ip的访问次数来进行过滤


ls  -lh /var/log/httpd/access_log 8.5k


wc -l /var/log/httpd/access_log 31行


ab -c 100 -n 100000 http://192.168.4.20/


dos攻击:模拟100人访问了网站10万次,这时刷新页面就会很卡。


tips:如果下标是变量就不能用引号;会把它当字符串的!先不加,结果不对了再加这样就不会错了.


awk '{IP["$1"]++}END{for (i in IP) {print IP[i],i}}' /var/log/httpd/access_log 


63445 $1



awk '{IP[$1]++}END{for (i in IP) {print IP[i],i}}' /var/log/httpd/access_log |sort -n 从小到大排序


219 ::1


63226 192.168.4.254


awk脚本应用案例:


案例:


快速进行源码包安装


yum repolist | awk '/repolist/ {print $2}' |sed 's/,//g'


源码包装的软件不能用systemctl restart (enable) 来进行管理。


netstat -ntulp |grep httpd


tcp6       0      0 :::80                   :::*                    LISTEN      8002/httpd


netstat -ntulp |grep :80


tcp6       0      0 :::80                   :::*                    LISTEN      8002/httpd


netstat -ntulp |grep :80


[ $? -ne 0 ]  && /usr/local/nginx/sbin/nginx




a=$(netstat -ntulp |awk '/:80/{print $7}'|cut -d/ -f2)




[ $a == "httpd" ] && systemctl stop $a


b=(netstat -ntulp |awk '/:80/{print $7}'|cut -d/ -f2|cut -d: -f1)


启动脚本niginx(case)





看本机状态信息


uptime |awk '{print$9,$10,$11}'


ifconfig eth0 | awk '/RX p/{print $5}'


ifconfig eth0 | awk '/TX p/{print $5}'


free |awk '/Mem/{print $NF}'


df | awk '/\/$/{print $4}'


cat /etc/passwd |wc -l


who |wc -l


rpm -qa |wc -l




$!最后一个后台进程的进程号。   



防止远程ssh暴力破解密码


awk '/Failed/ && /invalid/{print $13}' /var/log/secure |awk '{a[$1]++}END{for (i in a){print i,a[i]}}'


awk '/Failed/ && $9!~/invalid/{print $11}' /var/log/secure |awk '{a[$1]++}END{for (i in a){print i,a[i]}}'


  1 #!/bin/bash


  2 a=$(awk '/Failed/ && $9!~/invalid/{print $11}' /var/log/secure |awk '{a[$1]++}END{for (i in a){print i,a[i]}}'|awk '{if($2>5){print$1}}')


  3 


  4 b=$(awk '/Failed/ && $9!~/invalid/{print $11}' /var/log/secure |awk '{a[$1]++}END{for (i in a){print i,a[i]}}'|awk '{if($2>5){print$1}}')


  5 echo $a


  6 echo $bshell脚本知识储备汇总

语言类型

强类型:定义变量必须指定类型;

参与的运算必须要符合类型要求(字符串不能和数值相加等);

调用未声明变量会报错

弱类型:定义变量无需指定类型;

默认为字符型参与运算时会自动进行类型转换;

变量无需事先定义也可调用(空)

解释器:

shell是解释器bash是可用的具体解释器(shell是车这个概念bash是宝马车)

bash  sh ksh(需自己安装) tcsh csh等

bash的基本特性:快捷键;Tab补齐命令和路径;history;命令别名;标准输入输出;重定向;管道;

Redhat6选项不可Tab 需要装bash-completion这个包才能Tab

更改用户shell环境(解释器):   useradd -s 创建用户时

usermod -s 改已存在用户

vim /etc/passwd 改配置

history:查看条数: grep HISTSIZE /etc/profile

    echo $HISTSIZE

    history | wc -l

      history | tail

调用:     !1028

    !cat

清空(两条命令必须同时执行): history -c 

    > ~/.bash_history  空重定向到HISTFILE文件

别名alias: grep '^alias' ~/.bashrc 家目录下的隐藏文件

unalias 取消

重定向:

(正确覆盖输出);

>> (正确追加输出);

2> (错误覆盖输出);

2>> (错误覆盖输出);

&>       (正确错误覆盖输出);

&>>  (正确错误追加输出);

>&2      把正确变成错误输出

<; (导入文件)

tips: 写脚本可以上来就date >(>>) xx.log  再在里面把命令执行错误的信息2>(>>) xx.log以执行时间

进行区分了方便查看某次执行时的错误

非交互式写邮件三种:

mail -s biaoti user <<EOF

XX

YY

EOF

echo haha | mail -s biaoti user  管道

mail -s biaoti user < mail.txt 导入写好文件

脚本的执行方式:

/root/first.sh   ./first.sh 绝对路径(相对)执行 需+x

bash、sh /root/first.sh 解释器执行(相当于作为参数)不需+x  开了子进程pstree可以验证

source、./root/first.sh 解释器执行(相当于作为参数) 不需+x  当前进程执行

(用于使改的配置马上生效而不重启,相当于重读配置文件。

shell变量:

变量的类型

储值类型:

数值型  :整数型;浮点型;复数。。。

字符型  :

生效范围:

本地变量:当前Shell环境中有效,而在子Shell环境中无法直接使用

局部变量:

全局变量:

使用类型:

自定义变量: a=1 echo ${a}10  110 本地变量

TIPS:查看变量时,若变量名称与后面要输出的字符串连在一起,则应该以{}将变量名括起来以便区分

环境变量:  PWD、USER、HOME、SHELL、HISTSIZE、HISTFILE、PATH、PS1一级提示符、

PS2二级提示符、LOGNAME登录名 RANDOM HOSTNAME TERM记录终端类型

环境变量相关文件:(注意开机时读取文件顺序)

全局文件为/etc/profile,对所有用户有效

用户文件为~/.bash_profile,仅对指定的用户有效

env可查看所有环境变量:

set可查看所有变量

位置变量: $1、$2、$10、……

预定义变量: $0、$$、$?、$#、$*、$@

#!/bin/bash

echo $0                                        //脚本的名称

echo $1                                        //第一个参数

echo $2                                        //第二个参数

echo $*                                        //所有参数 当成一个整体字符串

echo $@           //所有参数 每个参数为独立字符串

echo $#                                        //所有的综合

echo $$                                        //当前进程的进程号

echo $?                                        //上一个命令执行状态返回值

shift 3 //换岗 踢掉参数(默认为一个)

使用read命令从键盘读取变量值

read -p "请输入。。:" i  用户输入的值赋予变量i

将回显功能关闭(stty -echo)  输password时

将回显功能恢复(stty echo)   输完password时

 

使用export发布全局变量

export PATH="$PATH:/usr/local/haha/bin"临时加了个可执行路径。

export XX="1234" 

变量的定义: 名称只能为字母数字下划线且只能以数字和下划线开头

a=123 字符串

a=$b 引用其他变量的值为自己的值

a=$(date) 命令结果引用为自己的值

三种引号对赋值的影响:

强引用: 单引号  界定一个完整字符串

弱引用: 双引号 界定一个完整的字符串且屏蔽特殊符号

命令结果引用:  反撇号或$() 引用命令执行的结果赋予变量

变量的调用

调用变量时,若变量名称与后面输出的字符串连在一起,以{}将变量名括起来以便区分

shell的数值运算

整数运算:

expr \*

$[]或$(()) 运算符两侧可以无空格 引用变量可省略 $

let 不显示结果

expr或$[]、$(())方式只进行运算,并不会改变变量的值;而let命令可以直接对变量值做运算再保存新的值

let X++ X-- X+=7 X-=7 X*=7 X/=7 X%=7  强大!!

小数运算:

bc scale=N 小数位的长度

echo 'scale=4;12.34-5.678' | bc 

6.662  虽然小数位长为4但运算数值最长为3 也只显示3位。

tips:bc支持条件测试正确返回1错误返回0 与$?相反

条件测试

“test 表达式”或者[ 表达式 ]都可以,表达式两边至少要留一个空格。注意空格空格空格!

[  $USER == "root"  ] 等号两边有空格(没有就相当字符串a) 表达式和[]间有空格!!!

字符串匹配

== != -z 检查变量的值是否为空    -n或!-z 检查变量的值是否非空

比较整数值的大小

eq

ne

gt

ge

lt

le

识别文件/目录的状态

-e

-d

-f

-r

-w

-x

多个条件/操作的逻辑组合

&&,逻辑与 第一个为假后面都不用执行

||,逻辑或 第一个为真后面都不用执行

!,逻辑非

短路运算:[ $X -gt 10 ] && echo "YES" || echo "NO"

  第一个为真必须yes而不会No了,因为前面整体为真了||运算已经结束。

if选择结构

单分支就直接: [ $X -gt 10 ] && echo "YES"

双分支: [ $X -gt 10 ] && echo "YES" || echo "NO"

if  [ 条件测试 ];then  

命令序列1

        else    命令序列2

fi

多分支:

if  [ 条件测试 ];then  

命令序列1 执行完就exit

elif

命令序列2 执行完就exit

        else   

命令序列3 执行完就exit

fi

案例:

检测/media/cdrom目录,若不存在则创建

检测并判断指定的主机是否可ping通

ping -c 3 -i 0.2 -W 3 192.168.4.5 

-c 3(3次)-i0.2间隔0.2秒-W 3反馈的超时秒数3

从键盘读取一个论坛积分,判断论坛用户等级

被阉割的选择结构case

case分支属于匹配执行的方式,它针对指定的变量预先设置一个可能的取值,判断该变量的实际取值是否与 预设的某一个值相匹配,如果匹配上了,就执行相应的一组操作,如果没有任何值能够匹配,就执行预先设 置的默认操作。

case  变量值  in

模式1)

    命令序列1 ;;

模式2)

    命令序列2 ;; 注意格式!!!必须是双分号。。

    .. ..

*)

    默认命令序列

esac

#!/bin/bash

case $1 in

redhat) //可以换成-n-i来代表选项做一个功能强大的命令出来

        echo "fedora";;

fedora)

        echo "redhat";;

    *)                                              //默认输出脚本用法

    echo "用法: $0 {redhat|fedora}"

esac

循环结构

for循环

for  变量名  in  值列表或 {1..5} 或 `seq 5` 或((i=1;i<=5;i++))

do

    命令序列

done

几个循环造数工具

{1..n}

seq 5(1开始到5) seq 2 10(2开始到10)seq 2 2 10(2开始中间跳2;即偶数)seq 1 2 10(奇数)

[$RANDOM%10+1] 随机产生1-10的数

((i=1;i<=5;i++))  初值1;条件<=5;步长为1

案例:

利用for循环来检测多个主机的存活状态

while循环

while  [条件测试]

do

    命令序列

done

死循环一般格式 while :

do

        命令序列

done

通常会写成死循环加退出的格式

while :

do

if[条件判断];then

        命令序列 && break

done

案例:  提示用户猜测一个随机数,直到才对为止

检测192.168.4.0/24网段,列出不在线的主机地址

shell函数

将一些需重复使用的操作,定义为公共的语句块,即可称为函数。通过使用函数,可以使脚本代码更加简洁,增强易 读性,提高Shell脚本的执行效率

相当于一段代码(一串命令)取了一个名字类似定义了alias

定义错了直接重新定义会覆盖,类似a=1;a=2后会覆盖前。

function  函数名 {

    命令序列

    .. ..

}

函数名() {

    命令序列

    .. ..

}

mycd(){                        //定义函数

> mkdir $1

> cd $1

> }

案例:颜色输出的命令:echo -e "\033[32;41mOK\033[0m"。3X为字体颜色分号隔开4X为背景颜色。

#!/bin/bash

cecho() {

    echo –e "\033[$1m$2\033[0m"

}

cecho 32 OK

cecho 33 OK

cecho 34 OK

cecho 35 OK

中断和退出

break可以结束整个循环;

continue结束本次循环,进入下一次循环;

exit结束整个脚本、

#!/bin/bash

for  i   in   {1..5}

do

        [  $i  -eq  3 ]&& break 

//这里将break替换为continue,exit分别测试脚本执行效果       

echo $i

done

echo  Over

break 12Over  continue1245Over  exit12

案例:求输入数的和输0退出

#!/bin/bash

while  read  -p  "请输入待累加的整数(0表示结束):"     x

do

    [ $x -eq 0 ]  &&  break

    SUM=$[SUM+x]

done

echo "总和是:$SUM"

案例:求1-20内所有数的平方跳过非6的倍数

#!/bin/bash

i=0

while  [ $i -le 20 ]

do

    let i++

    [ $[i%6] -ne 0 ]  &&  continue

    echo $[i*i]

done

exit 2

文本处理

字符串截取及切割

echo $变量名 | cut -b 起始位置-结束位置

echo $phone | cut -b 1-6 1-6

echo $phone | cut -b 8-    8到最后

echo $phone | cut -b 9 第9个

echo $phone | cut -b 3,5,8 第3 5 8个

expr substr "$变量名" 起始位置 长度

${变量名:起始位置:长度} 截取 (索引位置) ${phone:0:6} ${phone::6}

${变量名/old/new} 替换第一个

${变量名//old/new} 替换全部

${变量名#*关键词} (关键词也删)左向右 最短匹配删除 ${A#*:} 从左侧到第一个:的所有

${变量名##*关键词} 左向右 最长匹配删除 ${A##*:}从左侧到最后一个:的所有

${变量名%关键词*} 右向左 最短匹配删除 ${A%:*} 从右侧到第一个:的所有

${变量名%%关键词*} 右向左 最长匹配删除 ${A##*:}从右侧到最后一个:的所有

${变量名:-初值} 有初值就用初值 无初值就用赋予的初值

${XX:-123}xx无初值就赋予123为初值

随机密码 #!/bin/bash

x=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789

//所有密码的可能性是26+26+10=62(0-61是62个数字)

for i in {1..8}

do

num=$[RANDOM%62]

tmp=${x:num:1}

pass=${pass}$tmp 字符串相加

done

echo $pass

改扩展名 touch {a,b,c,d,e,f,g,h,i}.doc

#!/bin/bash

for FILE in `ls *.doc($1)`

do

    mv $FILE  ${FILE%.*}.txt("$2") (${FILE/doc/txt})  $1换成$2的扩展名

done

初值的处理 #!/bin/bash

read -p "请输入一个正整数:" x

x=${x:-1}

i=1; SUM=0

while [ $i -le $x ]

do

    let SUM+=i

    let i++

done

echo "从1到$x的总和是:$SUM"

#!/bin/bash

read  -p   "请输入用户名:"   user

read  -p   "请输入用户名:"   pass

[ -z $user ] && exit                    //如果无用户名,则脚本退出

pass=${pass:-123456}                    //如果用户没有输入密码,则默认密码为123456

useradd  $user

echo "$pass"  | passwd   --stdin   $pass

expect预期交互 :模拟人机交互过程(要提前对交互过程非常熟悉)

install  expect

#!/bin/bash

for i in 10 11 {1..245} seq 1 254

do

expect << EOF

spawn ssh -o StrictHostKeyChecking=no 172.25.0.$i   #//创建交互式进程   

expect "password:" { send "123456\r" }              #//自动发送密码

expect "#   { send "pwd > /tmp/$user.txt \r" }      #//发送命令

expect "#"  { send "exit\r" }

EOF

done

expect脚本的最后一行默认不执行

如果不希望ssh时出现yes/no的提示,远程时使用如下选项:

# ssh -o StrictHostKeyChecking=no server0

shell数组

整体赋值:a=(haha xixi hehe ) a[0]=haha 0是索引值为0的量

单个赋值:a[0]=haha  

  a[1]=xixi

  a[2]=hehe

文本处理三剑客grep 

三剑客都用''单引号!

正则表达式

字符匹配(单个字符).点 任意单个字符

[] [xyz] 集合,里面的任意单个字符

[a-z] [:lower:]

[A-Z] [:upper:]

[a-Z] [:alpha:]

[0-9] [:digit:]

[0-9a-Z][:alnum:]

[:space:]

[^] [^xyz] 集合取反

[^a-z]

[^A-Z]

[^a-Z]

[^0-9]

[^0-9a-Z]

|(扩展)x|y 或者

匹配次数 * 前一个字符出现任意次

\{n,m\} 前一字符出现n到m次

\{n} 前一字符出现n次

\{n,\} 前一字符至少出现n次

\(\) 保留(复制)后向引用

次数匹配(扩展) 至少出现1次

出现0次或1次

{n}  出现n次

{n,} 至少出现n次

{n,m} 出现n到m次

{0,m} 至多出现m次

位置锚定 ^ 匹配行首(以什么开头)

$ 匹配行尾(以什么结尾)

\> 词尾

\< 词首

\b(扩展) 单词边界

^$

^[[:space:]]*$ 

后向引用 () 组合为整体,保留(复制)

分组(复制) ( )复制  \1 \2 \3 粘贴

\B 匹配非单词边界“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。

? 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的

非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串

例如,对于字符串“oooo”,“o+”将尽可能多地匹配“o”,得到结果[“oooo”]

而“o+?”将尽可能少地匹配“o”,得到结果 ['o', 'o', 'o', 'o']

正则表达式过滤案例:

egrep -c '/sbin/nologin$' /etc/passwd = egrep '/sbin/nologin$' /etc/passwd | wc -l  内置了统计功能

egrep -vc '/sbin/nologin$' /etc/passwd  

egrep -cv '.' /etc/rc.local 统计空行

egrep -c '^$' /etc/rc.local 统计空行

egrep -c ".*" /etc/httpd/conf/httpd.conf   //总行数

egrep -c "#" /etc/httpd/conf/httpd.conf   //含注释的行数

egrep -c -v '#|^$'  /etc/httpd/conf/httpd.conf 有效配置行(多数配置文件适用)

egrep -v '#|^$'  /etc/httpd/conf/httpd.conf > httpd.conf.min 保存有效配置行

egrep  '^ .+|^[^#]' test5-19-1.sh  脚本里真正的有效配置的行?具体情况具体分析啊

egrep -m 2 '/sbin/nologin$' /etc/passwd 只显示匹配到的前2行

  egrep '.*' /etc/rc.local 匹配所有

egrep 'exec(ution)*' /etc/rc.local 包含exec 和execution的行

ifconfig | egrep '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}' 包含MAC地址

MAC03="20:69:74:R2:C5:27"  

判断mac地址是否有效

echo $MAC03 | egrep -q '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}' && echo "有效" || echo "无效"

过滤有效的邮箱地址

egrep '[0-9a-zA-Z_.]{3,}@[0-9a-zA-Z.-]{2,}(\.[0-9a-zA-Z-]{2,})+' mail.txt 

sort -t: -k3 -n /etc/passwd |tail -1 | cut -d: -f3 UID最大的用户名

精确匹配root用户(3种方式)

egrep '^\broot\b' /etc/passwd

egrep '^\<root\>' /etc/passwd  

awk -F: '$1=="root"' /etc/passwd

如果root存在显示它的解释器

id root &> /dev/null && egrep '^\broot\b' /etc/passwd | cut -d: -f7

id root &> /dev/null && awk -F: '$1=="root"{print $NF}' /etc/passwd

egrep '\b[0-9]{2,3}\b' /etc/passwd  2-3位数字

某个脚本的有效语句:

egrep  '^[^#]' test5-19-1.sh | egrep '[^[:space:]]+.*#.*' 这个好恼火啊

egrep  '^[^#]|[^[:space:]]+.*#.*' test5-19-1.sh 

用户名同shell名的:

egrep '(^\b[a-Z0-9]+\b).*\1$' /etc/passwd

sync:x:5:0:sync:/sbin:/bin/sync

shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

halt:x:7:0:halt:/sbin:/sbin/halt

bash:x:1005:1025::/home/bash:/bin/bash

nologin:x:1008:1028::/home/nologin:/sbin/nologin

案例:传递两个文件作为参数给脚本计算这两个文件所有空白行之和

k1=$(egrep "^[[:space:]]*$" $1 | wc -l

k2=$(egrep "^[[:space:]]*$" $2 | wc -l

echo "$[k1+k2]"

root centos user1用户的默认shell和uid

/etc/grub2.cfg文件中某单词后面跟一个小括号的行

使用echo输出一个绝对路径egrep找出其基名和目录名

ifconfig结果中1-255之间的数值(大量用|或者)

ifconfig结果中的IP地址

文本处理三剑客sed:

逐行处理软件 读一行处理一行 逐行处理读一行处理一行。

sed [选项] '条件指令' 文件

选项:

-n 屏蔽默认输出

-r 支持扩展正则

-i 修改源文件

条件:

行号 4    4;5  4,5   4,+10  

4~2(第4行和后面步长为2的行)

sed -n '2~2P' sed.txt  偶数行

sed -n '1~2P' sed.txt  奇数行

sed  -n '$=' a.txt            //输出文件的行数

/正则/

sed  -n 'p' a.txt            //输出所有行,等同于cat a.txt

sed  -n '4p' a.txt            //输出第4行

sed  -n '4,7p' a.txt        //输出第4~7行

sed  -n '4,+10p' a.txt        //输出第4行及其后的10行内容

sed  -n '/^bin/p' a.txt        //输出以bin开头的行

sed  '/xml/!d' a.txt         //删除不包含xml的行,!符号表示取反

sed  '$d' a.txt                //删除文件的最后一行

sed  '/^$/d' a.txt             //删除所有空行

tips: sed 's/xml//g'     a.txt     //将所有的xml都删除(替换为空串)

sed -n 's/2017/xxxx/gp' sed.txt //替换所有并打印

sed 's/xml/XML/'  a.txt        //将每行中第一个xml替换为XML

sed 's/xml/XML/3' a.txt     //将每行中的第3个xml替换为XML

sed 's/xml/XML/g' a.txt     //将所有的xml都替换为XML

sed 's/xml//g'     a.txt     //将所有的xml都删除(替换为空串)

sed 's#/bin/bash#/sbin/sh#' a.txt  //将/bin/bash替换为/sbin/sh 

(涉及到路径时用其他符合分隔)

sed '4,7s/^/#/'   a.txt         //将第4~7行注释掉(行首加#号)

sed 's/^#an/an/'  a.txt         //解除以#an开头的行的注释(去除行首的#号)

sed -n 's/.//2;s/.$//p' sed.txt 删除文件中每行的第二个、最后一个字

互换:(后向引用)

sed -rn 's/^(.)(.*)(.)$/\3\2\1/p' sed.txt 第一个、倒数第1个字符互换

sed -rn 's/^(.)(.)(.*)(.)(.)$/\1\4\3\2\5/p' sed.txt  第二第四互换

sed 's/[0-9]//' nssw.txt 删除文件中所有的数字

sed -r 's/[0-9]//g;s/^( )+//' nssw2.txt 删除所有数字、行首空格

sed -rn 's/([A-Z])/[\1]/gp' nssw.txt 为文件中每个大写字母添加括号

sed -rn 's/([a-Z0-9])/[\1]/gp' sed.txt  为文件中每个字母和数字添加括号

sed -rn 's/([a-Z0-9 ])/(\1)/gp' sed.txt 为文件中每个字母和数字空格添加括号

指令:

p 打印

d 删除

s 替换s/旧/新/g

a 指定行之后追加  

sed  '2a XX'   a.txt            //在第二行后面,追加XX

i 指定行之前插入

sed  '2i XX'   a.txt            //在第二行前面,插入XX

c 替换行

sed  '2c XX'   a.txt            //将第二行替换为XX

sed  '1c mysvr.tarena.com' /etc/hostname 修改主机名

脚本中修改配置文件时:

cp /etc/vsftpd/vsftpd.conf{,.bak}  给配置文件备份

/etc/hosts 这个文件非常有用:优先级比dns高 本地解析库

sed  -i  '$a 192.168.4.20  D  '  /etc/hosts

这一就可以ssh D 直接远程这个ip了。

sed  -i  '$a 192.168.4.20  www.baidu.com '  /etc/hosts

这样访问百度就快些了不用去找dns解析了 

文本处理三剑客awk:

awk [选项] '[条件]{指令}' 文件

多条编辑指令,可用分号分隔

默认将空格、制表符等作为分隔符

(cut sort 也是是默认空格Tab)

grep sed 不能直接打印某列要结合字符串的截取工具使用

-F 可指定分隔符  识别多种单个的字符

awk -F [:/] '{print $1,$10}' /etc/passwd

awk常用内置变量:

$0 文本当前行的全部内容

$1 文本的第1列

$3 文件的第3列,依此类推

NR 文件当前行的行号

NF 文件当前行的列数(有几列

awk -F: '{print NR,NF}' passwd.txt

awk -F: '{print NR,$NF}' /etc/passwd  永远打印最后一行!

awk -F: '{print NR,$NR}' /etc/passwd  第几行的时候就打印第几列!

可以打印常量:

awk -F: '{print $1,"的解释器:",$7}' /etc/passwd

打印常量必须要加双引号, 加逗号有空格,不加逗号无空格!

案例:

free |awk '/Mem/{print $NF}' 查看目前可用内存

ifconfig eth0 | awk '/RX p/{print $5}'  查看接收流量!

ifconfig eth0 | awk '/TX p/{print $5}'  查看发送流量!

df -h | awk '/\/$/{print $4}' #:\/$代表对/转义!!看跟分区可用空间。

df  | awk '/\/$/{print $4}'  故意不加单位 在脚本中好当成数值比较!!!

df -h / | tail -1 | awk '{print $4}' 第一个/ 代表要看/(根)的情况 提取最后一行 给awk

在网页中查看脚本:远程监控服务器网卡 内存等等情况(相当于动态页面了)

rpm -qa | grep httpd

cp /root/share/test5-19-1.sh /var/www/cgi-bin/test5-19-1.html 必须放在脚本专用目录下

chmod +x /var/www/cgi-bin/test5-19-1.html   必须要加X不然访问不了要报404. 看到404就检查有没有X

systemctl stop firewalld

setenforce 0 

ip/cgi-bin/test5-19-1.html   访问验证!!!

tips:

要想使访问时换行显示,要在/var/www/cgi-bin/test5-19-1.html 给这个文件加<br>然后再重启服务!!

而不是改之前的脚本文件,思路一定要清晰啊小伙子。

tips:

如果cp -P test5-19-1.html /var/www/html/

http://192.168.4.20/test5-19-1.html

访问到的就不是脚本执行的结果了,就是html 代码执行的结果了。如下

#!/bin/bash echo "Content-type: text/html" echo "" ifconfig eth0 | awk '/netmask/{print "Ip:"$2}' echo "

" ifconfig eth0 | awk '/RX p/{print "接收流量:"$5}' echo "

" ifconfig eth0 | awk '/TX p/{print "发送流量:"$5}' echo "

" df | awk '/\/$/{print "根分区可用:"$4}' echo "

" free |awk '/Mem/{print "内存可用:"$NF}'

路径一定要是/var/www/cgi-bin/test5-19-1.html 才能访问到脚步执行结果。(在未修改配置文件前提下)

格式化输出:

awk处理的时机:

处理第一行之前做准备工作 只做预处理的时候,可以没有操作文件

awk 'BEGIN{print x+1}'           #x可以不定义,直接用,默认值位0

中间进行逐行处理

处理完最后一行做总结

awk  [选项]  ' BEGIN{指令} {指令} END{指令}'  文件

BEGIN{ } 行前处理,读取文件内容前执行,指令执行1次

{ } 逐行处理,读取文件过程中执行,指令执行n次

END{ } 行后处理,读取文件结束后执行,指令执行1次

统计系统中使用bash作为登录Shell的用户总个数

awk 'BEGIN{x=0}/bash$/{x++} END{print x}' /etc/passwd

awk '/bash$/{x++} END{print x}' /etc/passwd  与上条命令等效 x未定义默认为0

格式化输出/etc/passwd

awk -F: 'BEGIN{print "User\tUID\tHome"} \

                                {print $1 "\t"  $3  "\t"  $6}     \ 使用“\t”显示Tab制表位

                          END{print "Total",NR,"lines."}' /etc/passwd

awk -F: 'BEGIN{print "用户名   家目录   UID"} {print $1,$6,$3 x++} END{print "总共"x"行"}' /etc/passwd

awk -F: 'BEGIN{print "用户名","家目录","UID"} {print $1,$6,$3} END{print "总共"NR"行"}' /etc/passwd

awk -F: 'BEGIN{print "用户名","家目录","UID"} {print $1,$6,$3 x++} END{print "总用户量:"x }' /etc/passwd

END{print "总共"NR"行"}  END{print "总共"x"行"}  打印最后的时候:注意变量不要引号,常量要引号,这是个坑啊!!!

awk -F: 'BEGIN{print "用户名\t家目录\tUID"} {print $1"\t"$6"\t"$3 x++}END{print "总用户量:"x }' /etc/passwd | column -t

column -t 自动排版对齐!!!awk -F: '{print $1,$2,$3 }' /etc/passwd | column -t

awk处理条件:

正则

awk -F: '/bash$/{print}' /etc/passwd

awk -F: '$1~/(zhangsan|root)/{print $7}' /etc/passwd  (支持扩展的正则)

~代表匹配:$1匹配后面的正则。并且是包含就算,模糊匹配 #第一列包含root或zhangsan的打印第7列。

[root@D share]# awk -F: '$1~/^root/' /etc/passwd

root:x:0:0:root:/root:/bin/bash

roota:x:1004:1024::/home/roota:/bin/bash 模糊匹配

awk -F: '$7!~/nologin$/{print $1,$7}' /etc/passwd    !~不匹配

使用数值/字符串比较设置条件

比较符号:== != > >= < <=

awk -F: '$1=="root"' /etc/passwd   常量要引号  精确匹配用户是为root的 不包含aroot root arootb 等等。

awk -F: 'NR==3'   打印第三行 不写{指令}默认为打印整行(所有列)

awk -F: '$3>=1000' /etc/passwd  大于等于1000的都是普通用户。

awk -F: '$3>10  && $3<5'  /etc/passwd  

tips:带逻辑判断的 要写合理  不然的话不会报错但是脚本执行结果不对,就不好排错了。

seq 100 | awk '$1%6!=0'  不能被6整除的。

逻辑测试条件

seq 100 | awk '$1%6==0 && $1%5==0'  5和6的公倍数。

seq 100 | awk '$1%7==0 || $1~/7/' 7整除或者包含7

awk '/Failed/ && /invalid/' /var/log/secure  

数学运算

awk 'BEGIN{a++;print a}'  1

case:

[root@svr5 ~]# cat getupwd-awk.sh

#/bin/bash

A=$(awk -F:  '/bash$/{print $1}' /etc/passwd)        ## 提取符合条件的账号记录

for i in $A

do

grep $i /etc/shadow | awk -F: '{print $1,"-->",$2}'                

done

awk 命令内部不能直接调用shell中的外部变量 $1==$i 要用-v选项来重新定义调用 很麻烦。

awk流程控制(if,for,while)

   if判断:

单分支:if(判断){命令} #第一个{表示命令开始    第二个}代表命令结束

多分支:if(判断){命令}else{命令} #所有指令都要放在{}里!!!!

awk -F: '{if($3<=1000) {i++}} END{print i} ' /etc/passwd   #结构很重要哈!!不行就先把结构框架打出来再往里面添加数据!!

awk -F: '{if($3<=1000){x++}else{y++}} END{print x,y}' /etc/passwd  

   for循环:

awk数组:

awk 'BEGIN{a++;print a}'  1  未定义的变量对它++就是1!!!!a=a+1 a未出现过,为空 空+1=1

awk 'BEGIN{a[0]++;print a[0]}'

非常重要:awk 'BEGIN{a[0]=00;a[1]=11;a[2]=22; for(i in a){print i,a[i]}}' 

0 0

1 11

2 22结果证明:i取值不是a的值而是a的下标。a[i]才是数组a的值。

awk数组的下标除了可以使用数字,也可以使用字符串,字符串需要使用双引号:

awk 'BEGIN{a["192.168.4.254"]=11;print a["192.168.4.254"]}'  11

awk 'BEGIN{a[192.168.4.254]=11;print a[192.168.4.254]}'  11

案例:        

背景:

awk 统计每一个ip的访问次数来进行过滤

ls  -lh /var/log/httpd/access_log 8.5k

wc -l /var/log/httpd/access_log 31行

ab -c 100 -n 100000 http://192.168.4.20/

dos攻击:模拟100人访问了网站10万次,这时刷新页面就会很卡。

tips:如果下标是变量就不能用引号;会把它当字符串的!先不加,结果不对了再加这样就不会错了.

awk '{IP["$1"]++}END{for (i in IP) {print IP[i],i}}' /var/log/httpd/access_log 

63445 $1

awk '{IP[$1]++}END{for (i in IP) {print IP[i],i}}' /var/log/httpd/access_log |sort -n 从小到大排序

219 ::1

63226 192.168.4.254

awk脚本应用案例:

案例:

快速进行源码包安装

yum repolist | awk '/repolist/ {print $2}' |sed 's/,//g'

源码包装的软件不能用systemctl restart (enable) 来进行管理。

netstat -ntulp |grep httpd

tcp6       0      0 :::80                   :::*                    LISTEN      8002/httpd

netstat -ntulp |grep :80

tcp6       0      0 :::80                   :::*                    LISTEN      8002/httpd

netstat -ntulp |grep :80

[ $? -ne 0 ]  && /usr/local/nginx/sbin/nginx

a=$(netstat -ntulp |awk '/:80/{print $7}'|cut -d/ -f2)

[ $a == "httpd" ] && systemctl stop $a

b=(netstat -ntulp |awk '/:80/{print $7}'|cut -d/ -f2|cut -d: -f1)

启动脚本niginx(case)

看本机状态信息

uptime |awk '{print$9,$10,$11}'

ifconfig eth0 | awk '/RX p/{print $5}'

ifconfig eth0 | awk '/TX p/{print $5}'

free |awk '/Mem/{print $NF}'

df | awk '/\/$/{print $4}'

cat /etc/passwd |wc -l

who |wc -l

rpm -qa |wc -l

$!最后一个后台进程的进程号。   

防止远程ssh暴力破解密码

awk '/Failed/ && /invalid/{print $13}' /var/log/secure |awk '{a[$1]++}END{for (i in a){print i,a[i]}}'

awk '/Failed/ && $9!~/invalid/{print $11}' /var/log/secure |awk '{a[$1]++}END{for (i in a){print i,a[i]}}'

  1 #!/bin/bash

  2 a=$(awk '/Failed/ && $9!~/invalid/{print $11}' /var/log/secure |awk '{a[$1]++}END{for (i in a){print i,a[i]}}'|awk '{if($2>5){print$1}}')

  3 

  4 b=$(awk '/Failed/ && $9!~/invalid/{print $11}' /var/log/secure |awk '{a[$1]++}END{for (i in a){print i,a[i]}}'|awk '{if($2>5){print$1}}')

  5 echo $a

  6 echo $b

shell脚本知识储备汇总




语言类型


强类型:定义变量必须指定类型;


参与的运算必须要符合类型要求(字符串不能和数值相加等);


调用未声明变量会报错


弱类型:定义变量无需指定类型;


默认为字符型参与运算时会自动进行类型转换;


变量无需事先定义也可调用(空)


解释器:


shell是解释器bash是可用的具体解释器(shell是车这个概念bash是宝马车)


bash  sh ksh(需自己安装) tcsh csh等



bash的基本特性:快捷键;Tab补齐命令和路径;history;命令别名;标准输入输出;重定向;管道;


Redhat6选项不可Tab 需要装bash-completion这个包才能Tab





更改用户shell环境(解释器):   useradd -s 创建用户时


usermod -s 改已存在用户


vim /etc/passwd 改配置


history:查看条数: grep HISTSIZE /etc/profile


    echo $HISTSIZE


    history | wc -l


      history | tail


调用:     !1028


    !cat


清空(两条命令必须同时执行): history -c 


    > ~/.bash_history  空重定向到HISTFILE文件



别名alias: grep '^alias' ~/.bashrc 家目录下的隐藏文件


unalias 取消



重定向:


(正确覆盖输出);


>> (正确追加输出);


2> (错误覆盖输出);


2>> (错误覆盖输出);


&>       (正确错误覆盖输出);


&>>  (正确错误追加输出);


>&2      把正确变成错误输出


<; (导入文件)


tips: 写脚本可以上来就date >(>>) xx.log  再在里面把命令执行错误的信息2>(>>) xx.log以执行时间


进行区分了方便查看某次执行时的错误



非交互式写邮件三种:


mail -s biaoti user <<EOF


XX


YY


EOF


echo haha | mail -s biaoti user  管道


mail -s biaoti user < mail.txt 导入写好文件



脚本的执行方式:


/root/first.sh   ./first.sh 绝对路径(相对)执行 需+x


bash、sh /root/first.sh 解释器执行(相当于作为参数)不需+x  开了子进程pstree可以验证


source、./root/first.sh 解释器执行(相当于作为参数) 不需+x  当前进程执行


(用于使改的配置马上生效而不重启,相当于重读配置文件。



shell变量:


变量的类型


储值类型:


数值型  :整数型;浮点型;复数。。。


字符型  :


生效范围:


本地变量:当前Shell环境中有效,而在子Shell环境中无法直接使用


局部变量:


全局变量:



使用类型:


自定义变量: a=1 echo ${a}10  110 本地变量


TIPS:查看变量时,若变量名称与后面要输出的字符串连在一起,则应该以{}将变量名括起来以便区分


环境变量:  PWD、USER、HOME、SHELL、HISTSIZE、HISTFILE、PATH、PS1一级提示符、


PS2二级提示符、LOGNAME登录名 RANDOM HOSTNAME TERM记录终端类型


环境变量相关文件:(注意开机时读取文件顺序)


全局文件为/etc/profile,对所有用户有效


用户文件为~/.bash_profile,仅对指定的用户有效


env可查看所有环境变量:


set可查看所有变量



位置变量: $1、$2、$10、……


预定义变量: $0、$$、$?、$#、$*、$@


#!/bin/bash


echo $0                                        //脚本的名称


echo $1                                        //第一个参数


echo $2                                        //第二个参数


echo $*                                        //所有参数 当成一个整体字符串


echo $@           //所有参数 每个参数为独立字符串


echo $#                                        //所有的综合


echo $$                                        //当前进程的进程号


echo $?                                        //上一个命令执行状态返回值


shift 3 //换岗 踢掉参数(默认为一个)



使用read命令从键盘读取变量值


read -p "请输入。。:" i  用户输入的值赋予变量i


将回显功能关闭(stty -echo)  输password时


将回显功能恢复(stty echo)   输完password时


 


使用export发布全局变量


export PATH="$PATH:/usr/local/haha/bin"临时加了个可执行路径。


export XX="1234" 



变量的定义: 名称只能为字母数字下划线且只能以数字和下划线开头


a=123 字符串


a=$b 引用其他变量的值为自己的值


a=$(date) 命令结果引用为自己的值



三种引号对赋值的影响:


强引用: 单引号  界定一个完整字符串


弱引用: 双引号 界定一个完整的字符串且屏蔽特殊符号


命令结果引用:  反撇号或$() 引用命令执行的结果赋予变量



变量的调用


调用变量时,若变量名称与后面输出的字符串连在一起,以{}将变量名括起来以便区分


shell的数值运算


整数运算:


expr \*


$[]或$(()) 运算符两侧可以无空格 引用变量可省略 $


let 不显示结果


expr或$[]、$(())方式只进行运算,并不会改变变量的值;而let命令可以直接对变量值做运算再保存新的值


let X++ X-- X+=7 X-=7 X*=7 X/=7 X%=7  强大!!


小数运算:


bc scale=N 小数位的长度


echo 'scale=4;12.34-5.678' | bc 


6.662  虽然小数位长为4但运算数值最长为3 也只显示3位。


tips:bc支持条件测试正确返回1错误返回0 与$?相反


条件测试


“test 表达式”或者[ 表达式 ]都可以,表达式两边至少要留一个空格。注意空格空格空格!


[  $USER == "root"  ] 等号两边有空格(没有就相当字符串a) 表达式和[]间有空格!!!


字符串匹配


== != -z 检查变量的值是否为空    -n或!-z 检查变量的值是否非空



比较整数值的大小


eq


ne


gt


ge


lt


le


识别文件/目录的状态


-e


-d


-f


-r


-w


-x


多个条件/操作的逻辑组合


&&,逻辑与 第一个为假后面都不用执行


||,逻辑或 第一个为真后面都不用执行


!,逻辑非


短路运算:[ $X -gt 10 ] && echo "YES" || echo "NO"


  第一个为真必须yes而不会No了,因为前面整体为真了||运算已经结束。


if选择结构


单分支就直接: [ $X -gt 10 ] && echo "YES"


双分支: [ $X -gt 10 ] && echo "YES" || echo "NO"


if  [ 条件测试 ];then  


命令序列1


        else    命令序列2


fi


多分支:


if  [ 条件测试 ];then  


命令序列1 执行完就exit


elif


命令序列2 执行完就exit


        else   


命令序列3 执行完就exit


fi


案例:


检测/media/cdrom目录,若不存在则创建


检测并判断指定的主机是否可ping通


ping -c 3 -i 0.2 -W 3 192.168.4.5 


-c 3(3次)-i0.2间隔0.2秒-W 3反馈的超时秒数3


从键盘读取一个论坛积分,判断论坛用户等级



被阉割的选择结构case


case分支属于匹配执行的方式,它针对指定的变量预先设置一个可能的取值,判断该变量的实际取值是否与 预设的某一个值相匹配,如果匹配上了,就执行相应的一组操作,如果没有任何值能够匹配,就执行预先设 置的默认操作。


case  变量值  in


模式1)


    命令序列1 ;;


模式2)


    命令序列2 ;; 注意格式!!!必须是双分号。。


    .. ..


*)


    默认命令序列


esac


#!/bin/bash


case $1 in


redhat) //可以换成-n-i来代表选项做一个功能强大的命令出来


        echo "fedora";;


fedora)


        echo "redhat";;


    *)                                              //默认输出脚本用法


    echo "用法: $0 {redhat|fedora}"


esac






循环结构


for循环


for  变量名  in  值列表或 {1..5} 或 `seq 5` 或((i=1;i<=5;i++))


do


    命令序列


done




几个循环造数工具


{1..n}


seq 5(1开始到5) seq 2 10(2开始到10)seq 2 2 10(2开始中间跳2;即偶数)seq 1 2 10(奇数)


[$RANDOM%10+1] 随机产生1-10的数


((i=1;i<=5;i++))  初值1;条件<=5;步长为1


案例:


利用for循环来检测多个主机的存活状态



while循环


while  [条件测试]


do


    命令序列


done



死循环一般格式 while :


do


        命令序列


done


通常会写成死循环加退出的格式



while :


do


if[条件判断];then


        命令序列 && break


done


案例:  提示用户猜测一个随机数,直到才对为止


检测192.168.4.0/24网段,列出不在线的主机地址




shell函数


将一些需重复使用的操作,定义为公共的语句块,即可称为函数。通过使用函数,可以使脚本代码更加简洁,增强易 读性,提高Shell脚本的执行效率


相当于一段代码(一串命令)取了一个名字类似定义了alias


定义错了直接重新定义会覆盖,类似a=1;a=2后会覆盖前。




function  函数名 {


    命令序列


    .. ..


}




函数名() {


    命令序列


    .. ..


}


mycd(){                        //定义函数


> mkdir $1


> cd $1


> }


案例:颜色输出的命令:echo -e "\033[32;41mOK\033[0m"。3X为字体颜色分号隔开4X为背景颜色。


#!/bin/bash


cecho() {


    echo –e "\033[$1m$2\033[0m"


}


cecho 32 OK


cecho 33 OK


cecho 34 OK


cecho 35 OK









中断和退出


break可以结束整个循环;


continue结束本次循环,进入下一次循环;


exit结束整个脚本、


#!/bin/bash


for  i   in   {1..5}


do


        [  $i  -eq  3 ]&& break 


//这里将break替换为continue,exit分别测试脚本执行效果       


echo $i


done


echo  Over


break 12Over  continue1245Over  exit12


案例:求输入数的和输0退出


#!/bin/bash


while  read  -p  "请输入待累加的整数(0表示结束):"     x


do


    [ $x -eq 0 ]  &&  break


    SUM=$[SUM+x]


done


echo "总和是:$SUM"


案例:求1-20内所有数的平方跳过非6的倍数




#!/bin/bash


i=0


while  [ $i -le 20 ]


do


    let i++


    [ $[i%6] -ne 0 ]  &&  continue


    echo $[i*i]


done


exit 2



文本处理


字符串截取及切割


echo $变量名 | cut -b 起始位置-结束位置


echo $phone | cut -b 1-6 1-6


echo $phone | cut -b 8-    8到最后


echo $phone | cut -b 9 第9个


echo $phone | cut -b 3,5,8 第3 5 8个


expr substr "$变量名" 起始位置 长度


${变量名:起始位置:长度} 截取 (索引位置) ${phone:0:6} ${phone::6}


${变量名/old/new} 替换第一个


${变量名//old/new} 替换全部


${变量名#*关键词} (关键词也删)左向右 最短匹配删除 ${A#*:} 从左侧到第一个:的所有


${变量名##*关键词} 左向右 最长匹配删除 ${A##*:}从左侧到最后一个:的所有


${变量名%关键词*} 右向左 最短匹配删除 ${A%:*} 从右侧到第一个:的所有


${变量名%%关键词*} 右向左 最长匹配删除 ${A##*:}从右侧到最后一个:的所有


${变量名:-初值} 有初值就用初值 无初值就用赋予的初值


${XX:-123}xx无初值就赋予123为初值



随机密码 #!/bin/bash


x=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789


//所有密码的可能性是26+26+10=62(0-61是62个数字)


for i in {1..8}


do


num=$[RANDOM%62]


tmp=${x:num:1}


pass=${pass}$tmp 字符串相加


done


echo $pass


改扩展名 touch {a,b,c,d,e,f,g,h,i}.doc


#!/bin/bash


for FILE in `ls *.doc($1)`


do


    mv $FILE  ${FILE%.*}.txt("$2") (${FILE/doc/txt})  $1换成$2的扩展名


done



初值的处理 #!/bin/bash


read -p "请输入一个正整数:" x


x=${x:-1}


i=1; SUM=0


while [ $i -le $x ]


do


    let SUM+=i


    let i++


done


echo "从1到$x的总和是:$SUM"




#!/bin/bash


read  -p   "请输入用户名:"   user


read  -p   "请输入用户名:"   pass


[ -z $user ] && exit                    //如果无用户名,则脚本退出


pass=${pass:-123456}                    //如果用户没有输入密码,则默认密码为123456


useradd  $user


echo "$pass"  | passwd   --stdin   $pass




expect预期交互 :模拟人机交互过程(要提前对交互过程非常熟悉)



install  expect


#!/bin/bash


for i in 10 11 {1..245} seq 1 254


do


expect << EOF


spawn ssh -o StrictHostKeyChecking=no 172.25.0.$i   #//创建交互式进程   


expect "password:" { send "123456\r" }              #//自动发送密码


expect "#   { send "pwd > /tmp/$user.txt \r" }      #//发送命令


expect "#"  { send "exit\r" }


EOF


done


expect脚本的最后一行默认不执行


如果不希望ssh时出现yes/no的提示,远程时使用如下选项:


# ssh -o StrictHostKeyChecking=no server0


shell数组


整体赋值:a=(haha xixi hehe ) a[0]=haha 0是索引值为0的量


单个赋值:a[0]=haha  


  a[1]=xixi


  a[2]=hehe




文本处理三剑客grep 


三剑客都用''单引号!


正则表达式



字符匹配(单个字符).点 任意单个字符


[] [xyz] 集合,里面的任意单个字符


[a-z] [:lower:]


[A-Z] [:upper:]


[a-Z] [:alpha:]


[0-9] [:digit:]


[0-9a-Z][:alnum:]


[:space:]


[^] [^xyz] 集合取反


[^a-z]


[^A-Z]


[^a-Z]


[^0-9]


[^0-9a-Z]


|(扩展)x|y 或者


匹配次数 * 前一个字符出现任意次


\{n,m\} 前一字符出现n到m次


\{n} 前一字符出现n次


\{n,\} 前一字符至少出现n次


\(\) 保留(复制)后向引用





次数匹配(扩展) 至少出现1次


出现0次或1次


{n}  出现n次


{n,} 至少出现n次


{n,m} 出现n到m次


{0,m} 至多出现m次








位置锚定 ^ 匹配行首(以什么开头)


$ 匹配行尾(以什么结尾)


\> 词尾


\< 词首


\b(扩展) 单词边界


^$


^[[:space:]]*$ 



后向引用 () 组合为整体,保留(复制)


分组(复制) ( )复制  \1 \2 \3 粘贴




\B 匹配非单词边界“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。



? 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的


非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串


例如,对于字符串“oooo”,“o+”将尽可能多地匹配“o”,得到结果[“oooo”]


而“o+?”将尽可能少地匹配“o”,得到结果 ['o', 'o', 'o', 'o']




正则表达式过滤案例:





egrep -c '/sbin/nologin$' /etc/passwd = egrep '/sbin/nologin$' /etc/passwd | wc -l  内置了统计功能


egrep -vc '/sbin/nologin$' /etc/passwd  


egrep -cv '.' /etc/rc.local 统计空行


egrep -c '^$' /etc/rc.local 统计空行




egrep -c ".*" /etc/httpd/conf/httpd.conf   //总行数




egrep -c "#" /etc/httpd/conf/httpd.conf   //含注释的行数


egrep -c -v '#|^$'  /etc/httpd/conf/httpd.conf 有效配置行(多数配置文件适用)


egrep -v '#|^$'  /etc/httpd/conf/httpd.conf > httpd.conf.min 保存有效配置行


egrep  '^ .+|^[^#]' test5-19-1.sh  脚本里真正的有效配置的行?具体情况具体分析啊



egrep -m 2 '/sbin/nologin$' /etc/passwd 只显示匹配到的前2行


  egrep '.*' /etc/rc.local 匹配所有


egrep 'exec(ution)*' /etc/rc.local 包含exec 和execution的行


ifconfig | egrep '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}' 包含MAC地址


MAC03="20:69:74:R2:C5:27"  




判断mac地址是否有效


echo $MAC03 | egrep -q '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}' && echo "有效" || echo "无效"



过滤有效的邮箱地址


egrep '[0-9a-zA-Z_.]{3,}@[0-9a-zA-Z.-]{2,}(\.[0-9a-zA-Z-]{2,})+' mail.txt 




sort -t: -k3 -n /etc/passwd |tail -1 | cut -d: -f3 UID最大的用户名


精确匹配root用户(3种方式)


egrep '^\broot\b' /etc/passwd


egrep '^\<root\>' /etc/passwd  


awk -F: '$1=="root"' /etc/passwd




如果root存在显示它的解释器


id root &> /dev/null && egrep '^\broot\b' /etc/passwd | cut -d: -f7


id root &> /dev/null && awk -F: '$1=="root"{print $NF}' /etc/passwd




egrep '\b[0-9]{2,3}\b' /etc/passwd  2-3位数字


某个脚本的有效语句:


egrep  '^[^#]' test5-19-1.sh | egrep '[^[:space:]]+.*#.*' 这个好恼火啊


egrep  '^[^#]|[^[:space:]]+.*#.*' test5-19-1.sh 




用户名同shell名的:


egrep '(^\b[a-Z0-9]+\b).*\1$' /etc/passwd


sync:x:5:0:sync:/sbin:/bin/sync


shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown


halt:x:7:0:halt:/sbin:/sbin/halt


bash:x:1005:1025::/home/bash:/bin/bash


nologin:x:1008:1028::/home/nologin:/sbin/nologin


案例:传递两个文件作为参数给脚本计算这两个文件所有空白行之和


k1=$(egrep "^[[:space:]]*$" $1 | wc -l


k2=$(egrep "^[[:space:]]*$" $2 | wc -l


echo "$[k1+k2]"


root centos user1用户的默认shell和uid


/etc/grub2.cfg文件中某单词后面跟一个小括号的行


使用echo输出一个绝对路径egrep找出其基名和目录名


ifconfig结果中1-255之间的数值(大量用|或者)


ifconfig结果中的IP地址




文本处理三剑客sed:



逐行处理软件 读一行处理一行 逐行处理读一行处理一行。


sed [选项] '条件指令' 文件


选项:


-n 屏蔽默认输出


-r 支持扩展正则


-i 修改源文件


条件:


行号 4    4;5  4,5   4,+10  


4~2(第4行和后面步长为2的行)


sed -n '2~2P' sed.txt  偶数行


sed -n '1~2P' sed.txt  奇数行


sed  -n '$=' a.txt            //输出文件的行数




/正则/


sed  -n 'p' a.txt            //输出所有行,等同于cat a.txt


sed  -n '4p' a.txt            //输出第4行


sed  -n '4,7p' a.txt        //输出第4~7行


sed  -n '4,+10p' a.txt        //输出第4行及其后的10行内容


sed  -n '/^bin/p' a.txt        //输出以bin开头的行




sed  '/xml/!d' a.txt         //删除不包含xml的行,!符号表示取反


sed  '$d' a.txt                //删除文件的最后一行


sed  '/^$/d' a.txt             //删除所有空行


tips: sed 's/xml//g'     a.txt     //将所有的xml都删除(替换为空串)






sed -n 's/2017/xxxx/gp' sed.txt //替换所有并打印


sed 's/xml/XML/'  a.txt        //将每行中第一个xml替换为XML


sed 's/xml/XML/3' a.txt     //将每行中的第3个xml替换为XML


sed 's/xml/XML/g' a.txt     //将所有的xml都替换为XML


sed 's/xml//g'     a.txt     //将所有的xml都删除(替换为空串)




sed 's#/bin/bash#/sbin/sh#' a.txt  //将/bin/bash替换为/sbin/sh 


(涉及到路径时用其他符合分隔)




sed '4,7s/^/#/'   a.txt         //将第4~7行注释掉(行首加#号)


sed 's/^#an/an/'  a.txt         //解除以#an开头的行的注释(去除行首的#号)


sed -n 's/.//2;s/.$//p' sed.txt 删除文件中每行的第二个、最后一个字


互换:(后向引用)


sed -rn 's/^(.)(.*)(.)$/\3\2\1/p' sed.txt 第一个、倒数第1个字符互换


sed -rn 's/^(.)(.)(.*)(.)(.)$/\1\4\3\2\5/p' sed.txt  第二第四互换




sed 's/[0-9]//' nssw.txt 删除文件中所有的数字


sed -r 's/[0-9]//g;s/^( )+//' nssw2.txt 删除所有数字、行首空格




sed -rn 's/([A-Z])/[\1]/gp' nssw.txt 为文件中每个大写字母添加括号


sed -rn 's/([a-Z0-9])/[\1]/gp' sed.txt  为文件中每个字母和数字添加括号


sed -rn 's/([a-Z0-9 ])/(\1)/gp' sed.txt 为文件中每个字母和数字空格添加括号





指令:


p 打印


d 删除


s 替换s/旧/新/g


a 指定行之后追加  


sed  '2a XX'   a.txt            //在第二行后面,追加XX


i 指定行之前插入


sed  '2i XX'   a.txt            //在第二行前面,插入XX


c 替换行


sed  '2c XX'   a.txt            //将第二行替换为XX


sed  '1c mysvr.tarena.com' /etc/hostname 修改主机名


脚本中修改配置文件时:


cp /etc/vsftpd/vsftpd.conf{,.bak}  给配置文件备份



/etc/hosts 这个文件非常有用:优先级比dns高 本地解析库




sed  -i  '$a 192.168.4.20  D  '  /etc/hosts


这一就可以ssh D 直接远程这个ip了。


sed  -i  '$a 192.168.4.20  www.baidu.com '  /etc/hosts


这样访问百度就快些了不用去找dns解析了 


文本处理三剑客awk:



awk [选项] '[条件]{指令}' 文件


多条编辑指令,可用分号分隔


默认将空格、制表符等作为分隔符


(cut sort 也是是默认空格Tab)


grep sed 不能直接打印某列要结合字符串的截取工具使用



-F 可指定分隔符  识别多种单个的字符


awk -F [:/] '{print $1,$10}' /etc/passwd



awk常用内置变量:




$0 文本当前行的全部内容




$1 文本的第1列




$3 文件的第3列,依此类推




NR 文件当前行的行号




NF 文件当前行的列数(有几列



awk -F: '{print NR,NF}' passwd.txt


awk -F: '{print NR,$NF}' /etc/passwd  永远打印最后一行!


awk -F: '{print NR,$NR}' /etc/passwd  第几行的时候就打印第几列!


可以打印常量:



awk -F: '{print $1,"的解释器:",$7}' /etc/passwd


打印常量必须要加双引号, 加逗号有空格,不加逗号无空格!



案例:


free |awk '/Mem/{print $NF}' 查看目前可用内存


ifconfig eth0 | awk '/RX p/{print $5}'  查看接收流量!


ifconfig eth0 | awk '/TX p/{print $5}'  查看发送流量!


df -h | awk '/\/$/{print $4}' #:\/$代表对/转义!!看跟分区可用空间。


df  | awk '/\/$/{print $4}'  故意不加单位 在脚本中好当成数值比较!!!


df -h / | tail -1 | awk '{print $4}' 第一个/ 代表要看/(根)的情况 提取最后一行 给awk



在网页中查看脚本:远程监控服务器网卡 内存等等情况(相当于动态页面了)


rpm -qa | grep httpd


cp /root/share/test5-19-1.sh /var/www/cgi-bin/test5-19-1.html 必须放在脚本专用目录下


chmod +x /var/www/cgi-bin/test5-19-1.html   必须要加X不然访问不了要报404. 看到404就检查有没有X


systemctl stop firewalld


setenforce 0 


ip/cgi-bin/test5-19-1.html   访问验证!!!


tips:


要想使访问时换行显示,要在/var/www/cgi-bin/test5-19-1.html 给这个文件加<br>然后再重启服务!!


而不是改之前的脚本文件,思路一定要清晰啊小伙子。


tips:


如果cp -P test5-19-1.html /var/www/html/


http://192.168.4.20/test5-19-1.html


访问到的就不是脚本执行的结果了,就是html 代码执行的结果了。如下


#!/bin/bash echo "Content-type: text/html" echo "" ifconfig eth0 | awk '/netmask/{print "Ip:"$2}' echo "


" ifconfig eth0 | awk '/RX p/{print "接收流量:"$5}' echo "


" ifconfig eth0 | awk '/TX p/{print "发送流量:"$5}' echo "


" df | awk '/\/$/{print "根分区可用:"$4}' echo "


" free |awk '/Mem/{print "内存可用:"$NF}'


路径一定要是/var/www/cgi-bin/test5-19-1.html 才能访问到脚步执行结果。(在未修改配置文件前提下)




格式化输出:



awk处理的时机:



处理第一行之前做准备工作 只做预处理的时候,可以没有操作文件


awk 'BEGIN{print x+1}'           #x可以不定义,直接用,默认值位0


中间进行逐行处理




处理完最后一行做总结




awk  [选项]  ' BEGIN{指令} {指令} END{指令}'  文件


BEGIN{ } 行前处理,读取文件内容前执行,指令执行1次


{ } 逐行处理,读取文件过程中执行,指令执行n次


END{ } 行后处理,读取文件结束后执行,指令执行1次


统计系统中使用bash作为登录Shell的用户总个数


awk 'BEGIN{x=0}/bash$/{x++} END{print x}' /etc/passwd


awk '/bash$/{x++} END{print x}' /etc/passwd  与上条命令等效 x未定义默认为0






格式化输出/etc/passwd


awk -F: 'BEGIN{print "User\tUID\tHome"} \


                                {print $1 "\t"  $3  "\t"  $6}     \ 使用“\t”显示Tab制表位


                          END{print "Total",NR,"lines."}' /etc/passwd


awk -F: 'BEGIN{print "用户名   家目录   UID"} {print $1,$6,$3 x++} END{print "总共"x"行"}' /etc/passwd


awk -F: 'BEGIN{print "用户名","家目录","UID"} {print $1,$6,$3} END{print "总共"NR"行"}' /etc/passwd


awk -F: 'BEGIN{print "用户名","家目录","UID"} {print $1,$6,$3 x++} END{print "总用户量:"x }' /etc/passwd


END{print "总共"NR"行"}  END{print "总共"x"行"}  打印最后的时候:注意变量不要引号,常量要引号,这是个坑啊!!!


awk -F: 'BEGIN{print "用户名\t家目录\tUID"} {print $1"\t"$6"\t"$3 x++}END{print "总用户量:"x }' /etc/passwd | column -t


column -t 自动排版对齐!!!awk -F: '{print $1,$2,$3 }' /etc/passwd | column -t




awk处理条件:


正则


awk -F: '/bash$/{print}' /etc/passwd


awk -F: '$1~/(zhangsan|root)/{print $7}' /etc/passwd  (支持扩展的正则)


~代表匹配:$1匹配后面的正则。并且是包含就算,模糊匹配 #第一列包含root或zhangsan的打印第7列。


[root@D share]# awk -F: '$1~/^root/' /etc/passwd


root:x:0:0:root:/root:/bin/bash


roota:x:1004:1024::/home/roota:/bin/bash 模糊匹配


awk -F: '$7!~/nologin$/{print $1,$7}' /etc/passwd    !~不匹配


使用数值/字符串比较设置条件




比较符号:== != > >= < <=


awk -F: '$1=="root"' /etc/passwd   常量要引号  精确匹配用户是为root的 不包含aroot root arootb 等等。


awk -F: 'NR==3'   打印第三行 不写{指令}默认为打印整行(所有列)


awk -F: '$3>=1000' /etc/passwd  大于等于1000的都是普通用户。


awk -F: '$3>10  && $3<5'  /etc/passwd  


tips:带逻辑判断的 要写合理  不然的话不会报错但是脚本执行结果不对,就不好排错了。


seq 100 | awk '$1%6!=0'  不能被6整除的。


逻辑测试条件



seq 100 | awk '$1%6==0 && $1%5==0'  5和6的公倍数。


seq 100 | awk '$1%7==0 || $1~/7/' 7整除或者包含7


awk '/Failed/ && /invalid/' /var/log/secure  


数学运算


awk 'BEGIN{a++;print a}'  1


case:


[root@svr5 ~]# cat getupwd-awk.sh


#/bin/bash


A=$(awk -F:  '/bash$/{print $1}' /etc/passwd)        ## 提取符合条件的账号记录


for i in $A


do


grep $i /etc/shadow | awk -F: '{print $1,"-->",$2}'                


done



awk 命令内部不能直接调用shell中的外部变量 $1==$i 要用-v选项来重新定义调用 很麻烦。


awk流程控制(if,for,while)



   if判断:


单分支:if(判断){命令} #第一个{表示命令开始    第二个}代表命令结束


多分支:if(判断){命令}else{命令} #所有指令都要放在{}里!!!!


awk -F: '{if($3<=1000) {i++}} END{print i} ' /etc/passwd   #结构很重要哈!!不行就先把结构框架打出来再往里面添加数据!!


awk -F: '{if($3<=1000){x++}else{y++}} END{print x,y}' /etc/passwd  






   for循环:




awk数组:


awk 'BEGIN{a++;print a}'  1  未定义的变量对它++就是1!!!!a=a+1 a未出现过,为空 空+1=1


awk 'BEGIN{a[0]++;print a[0]}'


非常重要:awk 'BEGIN{a[0]=00;a[1]=11;a[2]=22; for(i in a){print i,a[i]}}' 


0 0


1 11


2 22结果证明:i取值不是a的值而是a的下标。a[i]才是数组a的值。


awk数组的下标除了可以使用数字,也可以使用字符串,字符串需要使用双引号:


awk 'BEGIN{a["192.168.4.254"]=11;print a["192.168.4.254"]}'  11


awk 'BEGIN{a[192.168.4.254]=11;print a[192.168.4.254]}'  11


案例:        


背景:


awk 统计每一个ip的访问次数来进行过滤


ls  -lh /var/log/httpd/access_log 8.5k


wc -l /var/log/httpd/access_log 31行


ab -c 100 -n 100000 http://192.168.4.20/


dos攻击:模拟100人访问了网站10万次,这时刷新页面就会很卡。


tips:如果下标是变量就不能用引号;会把它当字符串的!先不加,结果不对了再加这样就不会错了.


awk '{IP["$1"]++}END{for (i in IP) {print IP[i],i}}' /var/log/httpd/access_log 


63445 $1



awk '{IP[$1]++}END{for (i in IP) {print IP[i],i}}' /var/log/httpd/access_log |sort -n 从小到大排序


219 ::1


63226 192.168.4.254


awk脚本应用案例:


案例:


快速进行源码包安装


yum repolist | awk '/repolist/ {print $2}' |sed 's/,//g'


源码包装的软件不能用systemctl restart (enable) 来进行管理。


netstat -ntulp |grep httpd


tcp6       0      0 :::80                   :::*                    LISTEN      8002/httpd


netstat -ntulp |grep :80


tcp6       0      0 :::80                   :::*                    LISTEN      8002/httpd


netstat -ntulp |grep :80


[ $? -ne 0 ]  && /usr/local/nginx/sbin/nginx




a=$(netstat -ntulp |awk '/:80/{print $7}'|cut -d/ -f2)




[ $a == "httpd" ] && systemctl stop $a


b=(netstat -ntulp |awk '/:80/{print $7}'|cut -d/ -f2|cut -d: -f1)


启动脚本niginx(case)





看本机状态信息


uptime |awk '{print$9,$10,$11}'


ifconfig eth0 | awk '/RX p/{print $5}'


ifconfig eth0 | awk '/TX p/{print $5}'


free |awk '/Mem/{print $NF}'


df | awk '/\/$/{print $4}'


cat /etc/passwd |wc -l


who |wc -l


rpm -qa |wc -l




$!最后一个后台进程的进程号。   



防止远程ssh暴力破解密码


awk '/Failed/ && /invalid/{print $13}' /var/log/secure |awk '{a[$1]++}END{for (i in a){print i,a[i]}}'


awk '/Failed/ && $9!~/invalid/{print $11}' /var/log/secure |awk '{a[$1]++}END{for (i in a){print i,a[i]}}'


  1 #!/bin/bash


  2 a=$(awk '/Failed/ && $9!~/invalid/{print $11}' /var/log/secure |awk '{a[$1]++}END{for (i in a){print i,a[i]}}'|awk '{if($2>5){print$1}}')


  3 


  4 b=$(awk '/Failed/ && $9!~/invalid/{print $11}' /var/log/secure |awk '{a[$1]++}END{for (i in a){print i,a[i]}}'|awk '{if($2>5){print$1}}')


  5 echo $a


  6 echo $b

猜你喜欢

转载自blog.51cto.com/13659481/2120836