这里包括shell变量,shell条件判断,shell多进程,Fd和命名管道
目录
简介
shell条件测试
判断某一个条件是不是城里的, 格式有三种,分别是 1. test 2. [ ] 3. [[ ]] 分种类有三类,以下三类
1. 文件测试
#!/usr/bin/bash
back_dir=/var/mysql_back
#检查这个目录是否存在,如果不存在,则创建
if ! test -d $back_dir;then
mkdir -p $back_dir
fi
[]
和使用test是一致的,注意使用[]
的空格
- -e filename 如果 filename 存在,则为真[[ -e /var/log/syslog ]]
- -d filename 如果 filename 为目录,则为真[[ -d /tmp/mydir ]]
- -f filename 如果 filename 为常规文件,则为真[[ -f /usr/bin/grep ]]
- -L filename 如果 filename 为符号链接,则为真[[ -L /usr/bin/grep ]]
- -r filename 如果 filename 对于当前用户可读,则为真[[ -r /var/log/syslog ]]
- -w filename 如果 filename 可写,则为真[[ -w /var/mytmp.txt ]]
- -x filename 如果 filename 可执行,则为真[[ -L /usr/bin/grep ]]
- filename1 -nt filename2 如果 filename1 比 filename2 新,则为真[[ /tmp/install/etc/services -nt /etc/services ]]
filename1 -ot filename2 如果 filename1 比 filename2 旧,则为真[[ /boot/bzImage -ot arch/i386/boot/bzImage ]]
filePath="/root/test.txt" if [[ -e $filePath ]] then echo "file is exist" else echo "file is not exist" fi
2.数字比较判断
num1 -eq num2 等于 [[ 3 -eq $mynum ]]
num1 -ne num2 不等于 [[ 3 -ne $mynum ]]
num1 -lt num2 小于 [[ 3 -lt $mynum ]]
num1 -le num2 小于或等于 [[ 3 -le $mynum ]]
num1 -gt num2 大于 [[ 3 -gt $mynum ]]
num1 -ge num2 大于或等于 [[ 3 -ge $mynum ]]
a=10
b=20
if [[ $a -eq $b ]];then
echo "$a -eq $b : a 等于 b"
else
echo "$a -eq $b: a 不等于 b"
fi
3.字符串比较判断
注意字符串要加双引号
-z string 如果 string 长度为零,则为真[[ -z $myvar ]]
-n string 如果 string 长度非零,则为真[[ -n $myvar ]]
string1 == string2 如果 string1 与 string2 相同,则为真[[ $myvar == one two three ]]string1 != string2 如果 string1 与 string2 不同,则为真[[ $myvar != one two three ]]
#!/usr/bin/bash
#注意字符串要加双引号
str1="i am xiaoming"
str2="i am xiaoqiang"
if [[ $str1 == $str2 ]]
then
echo "咦!我俩咋相同"
else
echo "咦!我俩咋不一样"
fi
shell条件测试实例
shell1.sh
#!/usr/bin/bash
grep 'cat $1' *#从所有文件中找到含有字符串cat $1的东西
#判断是否有参数
if [ $# -eq 0 ];then
echo "no value can not exe `basename $0` file"
fi
#检查是不是文件
if [ !-f $1];then
echo "error file type"
exit
fi
for ip in `cat $1`
do
ping -c1 $ip&> /dev/null
if [ $? -eq 0 ]
then
echo "$ip is up"
else
echo "$ip is down"
fi
done
shell2.sh
输入一个用户名,如果存在,则提示退出,如果不存在则创建后提示成功
#!/usr/bin/bash
grep 'cat $1' *#从所有文件中找到含有字符串cat $1的东西
read -p "请输入用户名:" user
if id $user&.>/dev/null; then
echo "$user is already exists"
exit
else
useradd $user
if [ $? -eq 0 ];then
echo "$user creat success"
fi
fi
shell3.sh
磁盘报警及计划任务
#!/usr/bin/bash
df -h |grep '/$'
100.0G 53.2G 46.8G 53% /
df -h |grep '/$' | awk '{print $(NF-1)}'
53%
df -h |grep '/$' | awk '{print $(NF-1)}' | awk -F"%" '{print $1}'#去掉百分号
53
disk_mem=`df -h |grep '/$' | awk '{print $(NF-1)}' | awk -F"%" '{print $1}'`# set value
mail_user=alice
if [ $disk_mem -ge 90 ];then
#send a mail to user by title named disk war...
echo "`date+%F-%H` disk : ${disk_mem}%" |mail -s "disk war..." $mail_user
fi
这里可以添加计划任务 :每隔五分钟执行一次任务 crontab -e来执行 添加 */5 * * * * /bin/bash shell3.sh
shell4.sh
内存报警及计划任务
获取内存值得使用情况,这里使用到了bash -vx可以对脚本进行输出调试
#!/usr/bin/bash
#^代表行首,$代表行尾,表示以Mem开头
free -m |grep '^Mem'
#1 2 3 4 5
Mem: 8064 5276 2788 0 0
free -m | grep '^Mem:' | awk '{print $3}'
5298
mem1=`free -m | grep '^Mem:' | awk '{print $3}'`
mem2=`free -m | grep '^Mem:' | awk '{print $2}'`
mem3=$(({mem1}*100/mem2))
echo "当前占用${mem3}%"
war_file=/bin/war.txt
mail_user=alice
rm -rf $war_file #判断是否有残留
if [ $mem3 -ge 80 ];then
echo "`date+%F-%H` mem : ${mem3}%" |mail -s "disk war..." $mail_user
fi
if (( $mem3>80));then
echo "`date+%F-%H` mem : ${mem3}%" > $war_file
fi
#判断文件是否存在
if [ -f $war_file ];then
mail -s "disk war..." $mail_user < $war_file
rm -rf $war_file
fi
shell模式匹配 case
语法结构
case 变量 in
mode_1)
commond1
;;
mode_2)
commond2
;;
mode_3)
commond3
;;
*)#相当于c语言中的default
commond
esac
shell模式匹配for
2.1、循环遍历传入进来的参数
for x in $@
do
echo number $x
done
2.2、遍历内存中的数组
arr=("one" "two" 123 456)
for x in ${arr[@]}
do
echo number $x
done
2.3、修改IFS=\n,将cat的结果变成数组,然后再for
#!/bin/bash
# 由于cat 是以\n来区分行,所以需要修改IFS
oldIFS=$IFS
IFS="\\n"
# 将cat得到的文件内容,收集起来,加上()转化为数组
catLines=(`cat /etc/hosts`)
IFS=$oldIFS
for line in ${catLines[*]}
do
echo $line
done
2.4、遍历文件夹下的文件
#!/bin/bash
# 先收集成数组,这样只有文件名
catLines=($(ls /root))
for line in ${catLines[*]}
do
echo $line
done
# 直接读,这样line是完整路径
for line in /root/*
do
echo $line
done
shell模式判断实例
shell1.sh
#!/usr/bin/bash
#delet user
#首先学习检测一个输入是否为一个命令
command -v ls
alias ls='_bbf ls --color=auto'
read -p "input a username: " user
id $user &>/dev/null
if [$? -ne 0 ];then
echo "no such user:$user"
exit 1 #退出返回值为
fi
read -p "sure?{y/n}:" action
case $action in
"y"|"yes"|"Y")
userdel -r $user
echo "$user is delet!"
;;
"no")
echo "good"
: #返回为真,相当于ture命令
*)
echo "bad"
esac
shell2.sh 跳板机,堡垒主机
跳板机(Jump Server),也称堡垒机,是一类可作为跳板批量操作远程设备的网络设备,跳板机最核心的功能是以本机作为跳板来操作远程设备,较为普遍地是通过ssh协议实现远程管理,使用单位一般会自己开发或选择性地使用以下自动化运维工具:
生产环境不建议root权限登录 一般生产环境有两个限制: 1:不允许直接连接 2:不允许root用户账户登录
远程登录一般为user登录,登录方式为密码账户或者密钥登录 将一下脚本放入 当前用户的.bash_profile里面 在最后一行加入 shell2.sh脚本的全路径
#!/usr/bin/bash
#jumpserver
trap "" HUP INT OUIT TSTP #用来屏蔽系统的中断信号,什么也不执行,防止从脚本中跳出来,屏蔽退出命令
web1=192.168.0.1
web2=192.168.0.2
web3=192.168.0.3
#函数
menu(){
cat <<-EOF
+-------------------------------+
| 1. web1 |
| 2. web2 |
| 3. web3 |
+-------------------------------+
EOF
}
while :
do
clear
menu
echo -e "\e[1;32mimput number: \e[0m"#换行输出绿色打印信息 input number:,-en是不换行的写法
read num
#or read -p "input number: " num
case $num in
1)
ssh alice@$web1
;;
2)
ssh alice@$web2
;;
3)
ssh alice@$web3
;;
" ")
;;
*)
echo "input error "
break
done
shell3.sh for循环的实现ping主机1
#!/usr/bin/bash
> a.txt #重定向
for i in {2...254} #or for i in `cat ip.txt` #这里要执行cat ip.txt,所以要执行``或者${}
do
{
ip=192.168.0.$i
ping -c1 -W.1 $ip&> /dev/null #-c1 ping一次,-W.1等待0.1s
if [ $? -eq 0 ]
then
echo "$ip is up"|tee -a ip.txt #usr tee type creat a file to record ping history
else
echo "$ip is down"
fi
}& # 在(子shell)后台执行
done
wait #等待前面所有后台进程结束
echo "finish"
任何程序或者脚本,都可以在前面加个time来统计执行的时间 time /shell3.sh
shell4.sh for循环的实现创建用户2
#!/usr/bin/bash
while :
do
read -p "enter prefix & passwod & num" prefix pass num
printf "user information:
------------------------
prefix:$prefix
pass:$pass
num:$num
------------------------
"
read -p "are you sure ?:" action
#此处判断是否确认输入
if [ "$action"="y"];then
break
fi
done
for i in `seq -w $num`#等位补齐
do
user=$prefix$i
id $user &>/dev/null
if [ $? -eq 0 ];then #检测用户是否存在
echo "can not create $user ,already exists"
else
useradd $user
echo "pass" |passwd --stdin &user &>/dev/null
if [ $? -eq 0];then
echo "creat $user success"
fi
fi
done
echo "create user"
shell5.sh for循环的实现文件中批量创建用户3
user.txt
a 123
b 456
d 789
#!/usr/bin/bash
#是否有输入参数
if [ $# -eq 0];then
echo "nu value can not use: `basename $0` file"
exit 1
fi
#检查类型
if [ ! -f $1];then
echo "type wrong"
exit 2
fi
#希望for处理文件按回车分割,而不是空格或tab空格
#但是for确实是这样--空格分割或tab空格
#所以要重新定义分隔符
#IFS内部字段分隔符
#IFS=$'\n'
#应增加以下两行
#IFS='
#'
for line in `cat $1` #每一行,每一行取(错误写法)
do
#判断长度是否为0,这里for会自动筛选,则不需要这个
if [ ${#line} -eq 0 ];then
continue #break是跳出循环,continue是结束本次循环
fi
user= `echo "$line" |awk '{print $1}'`
pass= `echo "$line" |awk '{print $2}'`
id $user &>/dev/null
if [ $? -eq 0 ];then #检测用户是否存在
echo "can not create $user ,already exists"
else
useradd $user
echo "pass" |passwd --stdin &user &>/dev/null
if [ $? -eq 0];then
echo "creat $user success"
fi
fi
done
shell6.sh for循环的实现文件中批量创建用户3
#!/usr/bin/bash
#重定向,相当于删除原有存在的
#>ok.txt
#>fail.txt
read -p "please enter a new password" pass
for ip in `cat ip.txt` #这里要执行cat ip.txt,所以要执行``或者$()
do
{
ping -c1 -W1 $ip&> /dev/null #-c1 ping一次,-W1等待1s
if [ $? -eq 0 ]
then
ssh $ip "echo $pass |passwd --stdin root"
if [ $? -eq 0];then
echo "$ip is up"|tee -a ok_`date +%F`.txt #usr tee type creat a file to record ping history
else
echo "$ip is down" >> fail_`date +%F`.txt #追加,不替代
fi
else
echo "$ip is down" >>fail_`date +%F`.txt
fi
}& # 在(子shell)后台执行
done
wait #等待前面所有后台进程结束
echo "finish"
shell7.sh for实现ssh批量创建用户3
#!/usr/bin/bash
#重定向,相当于删除原有存在的
#>ok.txt
#>fail.txt
read -p "please enter a new password" pass
for ip in `cat ip.txt` #这里要执行cat ip.txt,所以要执行``或者$()
do
{
ping -c1 -W1 $ip&> /dev/null #-c1 ping一次,-W1等待1s
if [ $? -eq 0 ]
then
#sed -ri '/^#UseDNS/cUseDNS no ' /etc/ssh/sshd_config #从/etc/ssh/sshd_config文件中,通过sed -ri查找 ,/^#UseDNS搜索以#UseDNS开头, /c替换为UseDNS
#ssh $ip "echo $pass |passwd --stdin root"
ssh $ip "sed -ri '/^#UseDNS/cUseDNS no ' /etc/ssh/sshd_config"
#if [ $? -eq 0];then
# echo "$ip is up"|tee -a ok_`date +%F`.txt #usr tee type creat a file to record ping history
#else
# echo "$ip is down" >> fail_`date +%F`.txt
#fi
else
#echo "$ip is down" >>fail_`date +%F`.txt
fi
}& # 在(子shell)后台执行
done
wait #等待前面所有后台进程结束
echo "finish"
grep 'DNS' /ect.ssh_config #在某个文件中查找DNS并打印所得行
while应用,非常适合逐行处理文件!!!
普通的循环控制
#!/bin/bash while [[ i -lt 10 ]] do echo $i i=$[i + 1] done
读取文件的每一行
#!/bin/bash cat $1| while read hostLine do # 将hostLine转化为数组,第一个是ip,第二个是机器名称,第三个是密码 # 如果hostLine分隔符是IFS默认的分隔符一种' ' 、 tab ,则可以直接下面这种方式切割 ipArr=($hostLine) # 如果hostLine分隔符是",",要么临时修改IFS为",",要么将hostLine分隔符替换为' ' # ipArr=(${hostLine//,/' '}) IP=${ipArr[0]} NAME=${ipArr[1]} PASSWD=${ipArr[2]} echo "ip是:$IP hostname是:$NAME 密码是:$PASSWD" done
普通的循环控制
#!/bin/bash while : #:冒号返回为真 do echo $i i=$[i + 1] done
shell1.sh create user
#!/usr/bin/bash
# 读取user.txt文件,创建账号,其中user是读的一行,因为read是以换行符为结束
while read user
do
#判断用户是否存在
id $user &>/dev/null
if [ $? -eq 0];then
echo "user already exists"
else
useradd $user
if [ $? -eq 0 ];then
echo "$user ok"
else
echo "bad"
fi
fi
done < user .txt #输入重定向
shell2.sh 探索while的字段分隔符定义
#!/usr/bin/bash
# 读取user.txt文件,创建账号,其中line是读的一行,因为read是以换行符为结束
while read line
do
if [ ${#line} -eq 0 ];then
echo "---------------"
continue
fi
#判断用户是否存在
user =`echo $line|awk '{print $1}' `
pass =`echo $line|awk '{print $2}' `
id $user &>/dev/null
if [ $? -eq 0];then
echo "user already exists"
else
useradd $user
if [ $? -eq 0 ];then
echo "$user ok"
else
echo "bad"
fi
fi
done < $1 #输入重定向,读取一行,否则退出
echo
until应用,也非常适合处理文件!!!
普通的循环控制
#!/usr/bin/bash ip=192.168.0.1 until ping -c1 -W1 $ip&> /dev/null #ping 不通继续ping,使用while ping 成功继续ping do sleep 1 done echo "$ip is done"
shell多进程,命名管道和Fd
文件描述符FD
File Description 文件描述符 或者文件句柄
进程使用文件描述符来管理打开的文件
ll /proc/$$/fd #$$是当前进程的pid,在/proc/进程id号/fd
0 1 2 3 4 .....
0: standard input
1: standard output
2: standard error
4: 可用句柄
touch /file1
exec 6<> /file1 #打开文件,且控制文件描述符号为6
echo "hello" > /proc/$$/fd/6
cat /proc/$$/fd/6 #输出hello
cat /file1 #输出hello
exec 6<&- #关闭文件,且控制文件描述符号为6,如果打开后没有关闭,就删除了文件,该句柄依然在
再谈管道
匿名管道
管道也是个文件其实,用了就没了 |
命名管道
先进先出的管道,实现两个shell通讯
mkfifo /tmp/tmpfifo
cat <<-EOF
+--------------------------------- -+
0 1--stdout--stdin--->-0 1
|process | |process|
| 2 | 2
+-----------------------------------+
EOF
rpm -qa |grep bash
shell1.sh 多进程ping 无控制
#!/usr/bin/bash
# 读取user.txt文件,创建账号,其中line是读的一行,因为read是以换行符为结束
for i in {2...254} #or for i in `cat ip.txt` #这里要执行cat ip.txt,所以要执行``或者${}
do
{
ip=192.168.0.$i
ping -c1 -W.1 $ip&> /dev/null #-c1 ping一次,-W.1等待0.1s
if [ $? -eq 0 ]
then
echo "$ip is up"|tee -a ip.txt #usr tee type creat a file to record ping history
else
echo "$ip is down"
fi
}& # 在(子shell)后台执行,相当于并发执行 多进程
done
wait #等待前面所有后台进程结束
echo "finish"
shell2.sh 多进程创建用户
#!/usr/bin/bash
# 读取user.txt文件,创建账号,其中line是读的一行,因为read是以换行符为结束
for i in {1...1000} #or for i in `cat ip.txt` #这里要执行cat ip.txt,所以要执行``或者${}
do
{
user=ttt$i
useradd $user
echo "111" |pass --stdin $user $>/dev/null
if [ $? -eq 0 ]
then
echo "$user is up"|tee -a user.txt #usr tee type creat a file to record ping history
else
echo "$user is down"
fi
}& # 在(子shell)后台执行,相当于并发执行 多进程
done
wait #等待前面所有后台进程结束
echo "finish..."
shell3.sh 多进程ping 控制
当很多线程需要并发时,可以采用这个进行控制
#!/usr/bin/bash
# 读取user.txt文件,创建账号,其中line是读的一行,因为read是以换行符为结束
thread =5 #当很多线程需要并发时,可以采用这个进行控制
tmp_fifofile=/tmp/$$.fifo #当前pid命名
#创建管道
mkfifo $tmp_fifofile #创建管道文件
exec 8<> $tmp_fifofile #打开管道文件,并将句柄命名成8
rm $tmp_fifofile #删除文件,但是还保留句柄8
for i in `seq $thread` #for i in {1...$thread} #因为这里不识别变量,所有不能用这个判断
do
echo >&8 #操作文件描述符8 echo相当于回车
done
for i in {1...254} #or for i in `cat ip.txt` #这里要执行cat ip.txt,所以要执行``或者${}
do
read -u 8 # read -u 读取文件描述符8,有数据 读到了才进行下去
{
user=ttt$i
useradd $user
echo "111" |pass --stdin $user $>/dev/null
if [ $? -eq 0 ]
then
echo "$user is up"|tee -a user.txt #usr tee type creat a file to record ping history
else
echo "$user is down"
fi
echo >&8 #返回
}& # 在(子shell)后台执行,相当于并发执行 多进程
done
wait #等待前面所有后台进程结束
exec 8>&- #释放文件描述符8,不是管道
echo "finish..."