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、$$、$?、$#、$*、[email protected]
#!/bin/bash
echo $0 //脚本的名称
echo $1 //第一个参数
echo $2 //第二个参数
echo $* //所有参数 当成一个整体字符串
echo [email protected] //所有参数 每个参数为独立字符串
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列。
[[email protected] 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:
[[email protected] ~]# 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、$$、$?、$#、$*、[email protected]
#!/bin/bash
echo $0 //脚本的名称
echo $1 //第一个参数
echo $2 //第二个参数
echo $* //所有参数 当成一个整体字符串
echo [email protected] //所有参数 每个参数为独立字符串
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列。
[[email protected] 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:
[[email protected] ~]# 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、$$、$?、$#、$*、[email protected]
#!/bin/bash
echo $0 //脚本的名称
echo $1 //第一个参数
echo $2 //第二个参数
echo $* //所有参数 当成一个整体字符串
echo [email protected] //所有参数 每个参数为独立字符串
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列。
[[email protected] 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:
[[email protected] ~]# 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
原文地址:http://blog.51cto.com/13659481/2120836