基础
- shell脚本就是命令的集合,
ping -c1 www.baidu.com &> /dev/null && echo "baidu is up" || echo "baidu is down"- 前后两个&的含义不同,&>包括标准输出和错误输出,重定向到null去
- 程序就是由:指令(逻辑)+数据组成的
- /usr/bin/cat <<-EOF
111
test222
EOF -的作用是支持EOF的tab操作。不一定是EOF,可以换成其他的,只是格式样的
- 同样的,这种用法可以将你在屏幕上输入的内容重定向到一新文件中,命令行添加 > file1
- 系统级,/etc/profile /etc/bashrc
- 用户级,~/.bash_profile ~/.bashrc,当退出时会执行,~/.bash_logout ~/.bash_history
- su - xiaobai,也就是login shell 时,执行以上四个文件
- su xiaobai,也就是nologin shell时,执行 /etc/bashrc ~/.bashrc
- bash shell特点
- 命令历史记忆功能:!number,!string,!$上一条命令最后一个参数,!!上一个命令,^R搜索历史命令
data > test.txtdata |tee test.txttee不会节流,将结果打印出来
- 通配符(元字符)
- [] 表示只匹配满足其中条件的一个
- ()在子shell中执行 (cd /boot;ls) (umask 077;touch file1000)
- 集合,
cp -rv /etc/sysconfig/network-scripts/ifcfg-eth0{,.old}
- 意思是创建ifcfg-eth0和ifcfg.old两个文件
- \ 转义符,让元字符回归本意。只有紧跟的那个参数才会被转转义
- 只是使命令中的特定文字为红色,不影响命令外面的颜色
echo -e "\e[1;31mThis is a red text.\e[0m"
- 前景色,从31..37
- 背景色,从40..47
- 脚本中使用变量
read -p “Please input a username: “ user
当然,先运行一下 id $user &>/dev/null也是可以
if id $user &>/dev/null;then echo “user $user already exists” else useradd $user if [ $? -eq 0 ];then echo “$user is created” fi fi
- **""弱引用;''强引用,不管是不是变量**- disk_free= `df -Ph |grep '/$' |awk '{print $4}'`- disk_free2=$(df -Ph |grep '/$' |awk '{print $4}')6. 数字运算, `echo $[2**10]` ,或用expr,或是 `echo $((2**3))` ,或是 `let num=1+2;echo $num`6. 变量内容的删除和替换- ${url#*si} 删除变量url,从开始到si,取si后面的作为新的变量内容- ${url#*.} 从前往后,最短匹配;${url##*.} 从前往后,最长匹配贪婪匹配。一直到最后一个.的内容都被匹配上- ${url%.*};${url%%.*} 从后往前匹配1. 索引及切片,${url} 字符串是从0开始- ${url:5:5};${url:5}<br />```bash#!/bin/bashmem_used=`free -m | grep '^Mem:' |awk '{print $3}'`mem_total=`free -m | grep '^Mem:' |awk '{print $2}'`mem_present=$((mem_used*100/mem_total))war_file=/tmp/mem_war.txtrm -rf $war_fileif (($mem_percent>80));then#if [ $mem_percent -ge 80 ];then可以改成ge大于等于echo "`date +%F-%H` memory:${mem_precent}%" > $war_filefiif [ -f $war_file ];thenmail -s "mem warning..." alice < $war_filerm -rf $war_filefi
- 字符串最好引号引起来,`[ “$USER” = “root” ];echo $?
#...........#...........read -p "Please input number: " numwhile truedoif [[ !"$num" =~ ^[0-9]+$ || "$num" =~ ^0+$]];then #避免输入以0开头或多个0的break #跳出这个循环elseread -p "不是数字,请重新输入数值:" numfidone#同样,下面的判断也可以做read -p "Please input prefix: " prefixif [ -z "$prefix" ];then #字符串长度不能为0,为0的话就退出echo "error prefix"exitfifor i in `seq $num`douser=$prefix$iuseradd $userecho "123" |passwd --stdin $user &>/dev/nullif [ $? -eq 0 ];thenecho "$user is created."fidone
- ()子shell中执行
- (())数值比较,运算C语言
- $()命令替换
- $(())整数运算
- []条件测试
- [[]]条件测试,支持正则 =~
- $[]整数运算
- {}
- ${}
- until和while语法结构类似,不过两者含义相反。都适合写循环次数不固定的语句
- until当条件测试成立(条件测试为假),执行循环体
until [ 1 -eq 2 ]
do
date;sleep 1
done
vim install_apache.sh#!/bin/bash#install apache in centos7#v1.0 by xxx 2020-xx-xxgateway=192.168.10.1if ping -c1 www.baidu.com &>/dev/null;thenyum install -y ....sed -ri '/^SELINUX=/cSELINUX=disabled' /etc/selinux/configcurl http://127.0.0.1 &>/dev/nullif [ $? -eq 0 ];thenecho "Apacha ok..."fielif ping -c1 $gateway &>/dev/null;thenecho "网关正常,请检查dns或其他..."exitelseecho "check ip address!"fifi###read -p "确认开始安装KVM[y]" kvm_installif [ !"${kvm_install}"="y"];thenecho -e "$red_col输入不正确!$reset_col"exitfi
删除用户
read -p "Please input a username: " userid $user &>/dev/nullif [ $? -ne 0 ];thenecho "no such user: $user"exit 1 #第一种错,返回1firead -p "Are you sure[y/n]: " action#if [ "$action" != "y" -a "$action" != "Y" -a "$action" != "yes" -a "$action" != "YES" ];then# echo "good!"# exit#ficase "$action" iny|Y|yes|YES)userdel -r $userecho "$user is deleted!";;*)echo "error"esac#userdel -r $user && echo "$user is deleted!"
简单的JumpServer
跳板机属于内控堡垒机范畴,是一种用于单点登陆的主机应用系统。
使用alice用户登录到跳板机,然后脚本就运行了,所以脚本放在用户的家目录,
vim jumpserver.sh- 业务服务器不允许直接连接,通过允许从跳板机连接
- 业务服务器不允许root用户直接登录
#!/bin/bash#jumpservertrap "" HUP INT OUIT TSTP #捕捉键盘信号,然后什么操作也没有web1=192.168.10.1web2=192.168.10.2mysql1=192.168.10.3clearwhile :docat <<-EOF+----------------------------------------------------+|JumpServer ||1. web1 ||2. web2 ||3. mysql1 |+----------------------------------------------------+EOF# read -p "input number: " numecho -en "\e[1;32minput number: \e[0m" #n表示不要换行read numcase "$num" in1)ssh alice@$web1;;2)ssh alice@$web2;;3)ssh alice@$mysql1;;"");;*)echo "error"esacdone
将执行脚本的语句,加入到alice的bash_profile里,就可以登录时自动运行该脚本,
vim .bash_profile- 在ssh连接时,使用秘钥的方式,可以不需用户输入密码就可登录到其他Server
- ssh-copy-id root@192.168.10.1,相当于以root身份登录到192.168.10.1去
- 当然,脚本里也要改
实现系统工具箱
#!/bin/bash#system manage#v1.0menu() {cat <<-EOFh. helpf. disk partitiond. filesystem mountm. memoryu. system loadq. exitEOF} #函数,一个代码块,为了实现重复调用menuwhile : #表示为真,true也可以,循环。但exit同样可以退出doread -p "Please input[h for help]: " actioncase "$action" inh) clear;menu;;f) fdisk -l;;d) df -Th;;m) free -m;;u) uptime;;q)#exit #退出程序更直接break #退出循环;;"") ;;*) echo "error"esacdoneecho "Finish..."
- 实现多版本php安装。
vim main_install.sh通过一个主要的脚本来调用其他的安装脚本。就类似于ansible的剧本文件,main.yml来调用其他的剧本文件批量主机ping探测
#!/bin/bash>ip.txtfor i in {2..254} #`seq 2 254` 产生一个序列#for i in `cat ip.txt` ping的对象来自特定的一部分主机do{ip=192.168.122.$iping -c1 -W1 $ip &>/dev/null #-W表示超时1s就会停if [ $? -eq 0 ];thenecho "$ip" |tee -a ip.txtfi}& #意思是把括号内的程序放到后台去执行donewait #也是命令,意思是等待当前shell中的他前面的所有的后台进程结束echo "finish..."
批量创建用户
#!/usr/bin/bash#v1.0while :doread -p "Please enter prefix & passwod & num[tianyu 123 5]: " prefix pass numprintf "user information:-------------------------user prefix: $prefixuser password: $passuser number: $num-------------------------"read -p "Are you sure?[y/n]: " actionif [ "$action" != "y" ];thenbreakfidonefor i in `seq -w $num` #seq是等位补齐的douser=$prefixid $user &>/dev/nullif [ $? -eq 0 ];thenecho "user $user already exists."elseuseradd $userecho "$pass" |passwd --stdin $user &>/dev/null #进行创建用户并授予密码if [ $? -eq 0 ];thenecho "$user is created."fifidone
#!/usr/bin/bash#...if [ $# -eq 0 ];thenecho "usage: `basename $0` file."exit 1fiif [ ! -f $1 ];thenecho "error file!"exit 21fi#希望for处理文件按回车分割,而不是空格或tab。需要重新定义分隔符。#IFS内部字段分隔符,定义为回车。使用while循环就没这些IFS=$'\n' #或是直接敲回车也可for line in `cat $1` #line读取文件的每一行,相当于每一次把文件的一行赋值给linedoif [ ${#line} -eq 0 ];then #判断是否存在空行continue; #满足条件,后面的循环代码不再执行。是跳过本次循环,不是跳出循环fiuser=`echo "$line" |awk '{print $1}'` #把user和pass筛选出来pass=`echo "$line" |awk '{print $2}'` #然后下面就一样了#id $user &>/dev/nullif [ $? -eq 0 ];thenecho "user $user already exists!"elseuseradd $userecho "$pass" | passwd --stdin $user &>/dev/nullif [ $? -eq 0 ];thenecho "$user is created."fifidone
bash -vx create_user.sh user01.txt查看脚本运行过程使用while语法,以下:
#! /usr/bin/bash#v1.0while read line #是处理文件时,最常用的,逐行处理。不同于上面的fordoif [ ${#line} -eq 0 ]; thencontinue; #跳出本次循环,继续下一轮的循环操作。break;exitfiuser=`echo $line |awk'{print $1}'`pass=`echo $line |awk'{print $2}'`id $user &>/dev/nullif [ $? -eq 0 ]; thenecho "user $user already exists."elseuseradd $userecho "$pass" |passwd --stdin $user &>/dev/nullif [ $? -eq 0 ]; thenecho "$user is created."fifidone <$1 #输入重定向echo "ALL OK."
bash -vx create_user02.sh usertest.txt调试脚本ssh非交互登录
- 通过使用expect程序,
yum -y install expect
- ssh登录的时候提示error,在 vim /root/.ssh/known_hosts 文件中删除之前登录过的主机的指纹信息,再次连接就可。相当于重新记录客户机的指纹信息fingerprint
- 在脚本中将每一步遇到的交互和进行的操作都写上,
锦囊 vim expect_ssh.sh
```bash!/usr/bin/expect
set ip [lindex $argv 0] #第1个未知参数,相当于shell的$1 set user [lindex $argv 1] #第2个未知参数.需要在调用文件时传参 set password centos set timeout 5 #设置超时时间5s spawn ssh $user@$ip expect { “yes/no” { send “yes\r”; exp_continue } “password”: { send “$password\r” }; }interact #意思是停在服务端那边
expect “#” #当出现#符号的时候 send “useradd xiaobai\r” send “pwd\r” send “exit\r” expect eof #这行意思是,expect做完事之后就结束就退出了
#
下面以拷贝文件为例
spawn scp -r /etc/hosts $user@$ip:/tmp expect { “yes/no” { send “yes\r”; exp_continue } “password”: { send “$password\r” }; } expect eof
<a name="5IoYs"></a>### expect实现批量主机公钥推送- `vim gepip_push.sh````bash#! /usr/bin/bash>ip.txtpassword=centosrpm -q expect &>/dev/nullif [ $? -ne 0 ]; then #检查下expect是否安装,-ne不等于echo "installing expect..."yum -y install expect &>/dev/nullfiif [ ! -f ~/.ssh/id_rsa ]; thenecho "正在创建公钥,请稍候。。。"ssh-keygen -P "" -f ~/.ssh/id_rsafifor i in {2..254}do{ip=192.168.122.$iping -c1 -W1 $ip &>/dev/nullif [ $? -eq 0 ]; thenecho "$ip" >> ip.txt/usr/bin/expect <<-EOF #EOF包含的语句都用expect去解释set timeout 10spawn ssh-copy-id $ip #注意一定是tab键位,vim用set list可查看使用的什么键位expect {"yes/no" { send "yes\r"; exp_continue }"password:" { send "$password\r" }}expect eofEOFfi}& #在后台执行的donewaitecho "already finish~"
批量主机密码修改
vim passwd_chg.sh#! /usr/bin/bash#v1.0 by 2200-4-20read -p "Please enter a new password: " passfor ip in $(cat ip.txt)do{ping -c1 -W1 $ip &>/dev/nullif [ $? -eq 0 ]; thenssh $ip 'echo $pass |passwd --stdin root'if [ $? -eq 0 ]; thenecho "$ip" >>ok_`date +%F`.txtelseecho "$ip" >>fail_`date +%F`.txtfielseecho "$ip" >>fail_`date +%F`.txtfi}& #在后台执行的donewaitecho "already finish~"
批量远程主机ssh配置
vim modify_sshcon.sh#! /usr/bin/bash$v1.0 by xiaobaifor ip in `cat ip.txt`do{ping -c1 -W1 $ip >/dev/nullif [ $? -eq 0 ]; thenssh $ip "sed -ri '/^#UseDNS/cUseDNS no' /etc/ssh/sshd_config" #其实往文件中追加内容也可ssh $ip "sed -ri '/^#GSSAPIAuthentication/cGSSAPIAuthentication no' /etc/ssh/sshd_config"#c 含义是替换,上面两行是避免影响ssh连接远程的速度ssh $ip "systemctl stop firewalld;systemctl disable firewalld"ssh $ip "sed -ri '/^#SELINUX=/cSELINUX=disabled' /etc/sysconfig/selinux"ssh $ip "setenforce 0"fi}& #以上命令都是shell并发执行的donewaitecho "all ok~"
bash -n modify_sshcon.sh检查下语法chmod a+x modify_sshcon.sh- 通过在shell中执行脚本命令来复查上面的运行结果
for ip in cat ip.txt
do
ssh $ip “grep ‘DNS’ /etc/ssh/sshd_config”
done
for_while_unit
#! /usr/bin/bashfor i in {1..100}dolet sum=$sum+$i #let运算#let sum+=$idoneecho "sum: $sum"#########################################i=1while [ $i -le 100 ]dolet sum=$sum+$ilet i++doneecho "sum: $sum"#########################################i=1until [ $i -gt 100 ]dolet sum+=$ilet i++doneecho "sum: $sum"
命名管道实现并发控制(数量)
#! /usr/bin/bashjc=2 #进程数量tmp_fifofile=/tmp/$$.fifo #以当前pid命名的mkfifio $tmp_fifofileexec 8<> $tmp_fifofilerm $tmp_fifofile #删掉文件了,但不影响上面的描述符 8for i in `seq $jc`doecho >&8 #相当于操作文件描述符 8,从fd=8的文件中拿。循环的前2个是可以读取的;结束循环后会还回去donefor i in {1..254}doread -u 8 #u后面的是文件描述符fd{ip=192.168.0.$iping -c1 -W1 $ip &>/dev/nullif [ $? -eq 0 ]; thenecho "$ip is up~"fiecho >&8 #有借有还,借的就是上面read过来的fd。公园划船一个意思}&donewaitexec 8>& #释放fd,因为上面有打开,就有释放echo "all finish..."
- exec给文件一个描述符;exec关闭文件(释放文件句柄);如果FD未被释放,即使删除了原文件一样可以copy出来
- 管道也是文件。上面例子用的是匿名管道
shell数组变量
- 普通数组:只能使用整数作为数组索引(python:列表)
- books={linux shell awk openstack docker}
- echo ${books[3]}
- 关联数组:可以使用字符串作为数组索引(python:字典)
- declare -A info
- info=([name]=xiaobai [sex]=male [age]=20 [height]=180)
- echo ${info[age]}
- 获得数组的索引,
echo ${!info[@]},通过索引进行遍历
