Shell 编程入门笔记
【阿里云】爆款云产品,新客特惠全年最低价,云服务器低至0.4折起,11.1开售
【腾讯云】爆款1核2G云服务器首年48元,还有iPad Pro、Bose耳机、京东卡等你来抽!
【华为云】上云特惠巨划算,免单抽奖享豪礼
【七牛云】爆款云产品全年最低价,热门产品 0 元秒杀,参与抽奖赢新款 iPhone
一. Shell能做什么
- 自动化批量系统初始化程序(Update,软件安装,时区设置,安全策略。。。。)
- 自动化批量软件部署程序(LAMP,LNMP/,omcat,LVS,Nginx)
- 管理应用程序(KVM,集群管理扩容,MySQL)
- 日志分析处理程序(PV,UV,状态统计,grep/awk)
- 自动化备份/恢复程序(MySQL完全/增量备份+Crond)
- 自动化管理程序(批量远程修改密码,软件升级,配置更新)
- 配合Zabbix信息采集
- 自动化信息采集及监控程序(收集系统/应用的状态信息:CPU/Mem/Disk/Net/TCP Status/Apache/MySQL)
- 自动化扩容(增加云主机,业务上线)
- 俄罗斯方块,打印图形,算法排序实现
二. Shell规范
1. 脚本命名
脚本通常以.sh结尾
2.执行方式
sh
sh test.sh
bash
bash test.sh
以路径方式执行(需要脚本有执行权限)
chmod +x test.sh ./test.sh
3. 编写规范
第一行声明程序解释器,写明解释器程序所在路径
#!/usr/bin/bash
三. Bash Shell 基础特性
1. 命令和文件自动补齐
Tab键自动补齐
2. 命令历史记忆功能
- 上下键切换历史命令
!number
:执行第n条命令!string
:执行最近一条以string开头的命令!$
:上一条命令的最后一个参数!!
:上一条命令Ctrl+R
:搜索历史命令
3. 别名功能
alias
:查看当前Shell别名unalias
:取消当前Shell别名
4. 快捷键
Ctrl+R
:搜索历史命令Ctrl+D
:等于logout或exit,退出当前ShellCtrl+A
:光标移动到行首Ctrl+E
:光标移动到行尾Ctrl+L
:清屏,相当于clear命令Ctrl+U
:删除或剪切光标之前的命令。Ctrl+K
:删除或剪切光标之后的内容。Ctrl+S
:暂停屏幕输出Ctrl+Q
:恢复屏幕输出Ctl+C
:强制中止当前的命令Ctrl+Z
:暂停命令,并放入后台
5. 前后台作业控制
&
:后台执行命令,终端关闭后任务结束nohup
:后台执行命令,终端关闭后不终止screen
:后台执行命令Ctrl+C
:结束前台进程kill
:结束指定进程bg/fg
:进程转后台/前台
6. 输入输出重定向
>
:输出重定向到一个文件或设备 覆盖原来的文件\>!
:输出重定向到一个文件或设备 强制覆盖原来的文件>>
:输出重定向到一个文件或设备 追加原来的文件<
:输入重定向到一个程序2>
:将一个标准错误输出重定向到一个文件或设备 覆盖原来的文件 b-shell2>>
:将一个标准错误输出重定向到一个文件或设备 追加到原来的文件2>&1
:将一个标准错误输出重定向到标准输出&>
:将一个标准错误输出重定向到一个文件或设备 覆盖原来的文件|&
: 将一个标准错误 管道 输送 到另一个命令作为输入- 标准输入;代码为 0 ;或称为 stdin ;使用的方式为
<
- 标准输出:代码为 1 ;或称为 stdout;使用的方式为
1>
- 错误输出:代码为 2 ;或称为 stderr;使用的方式为
2>
7. 管道
|
:上一条命令的输出输入给下一条命令tee
:读取标准输入的数据,并将其内容输出成文件
8. 命令排序
;
:顺序地独立执行各条命令, 彼此之间不关心是否失败, 所有命令都会执行&&
: 顺序执行各条命令, 只有当前一个执行成功时候, 才执行后面的||
:顺序执行各条命令, 只有当前面一个执行失败的时候, 才执行后面的
9.通配符(元字符)
*
:匹配 0 个或匹配任意多个字符?
:匹配任意一个字符[]
:匹配括号中任意一个字符,如[abc] [a-z] [0-9] [a-zA-Z0-9] [^a-zA-Z0-9]()
:在子Shell中执行,不会对当前环境造成影线{}
:集合\
:转义元字符
四. Shell变量
1. 什么是变量?
用一个特定的字符串去表示不固定的内容
2.变量的类型
2.1. 自定义变量
- 定义变量:
变量名=变量值
,变量名必须以字母或下划线开头 - 引用变量:
$变量名
或${变量名}
- 查看变量:
echo $变量名
- 取消变量:
unset 变量名
- 作用范围:仅在当前Shell中有效
2.2. 系统环境变量
定义环境变量:
export 变量名=变量值
export 变量名
:将自定义变量转换成环境变量
- 引用环境变量:
$变量名
或${变量名}
查看环境变量:
echo $变量名
env
:显示所有环境变量
- 取消环境变量:
unset 变量名
- 作用环境范围:在当前Shell和子Shell中有效
2.3. 位置变量
$number
:第n个参数作为变量值赋给变量
2.4. 预定义变量
$0
:脚本名$*
:所有的参数$@
:所有的参数$#
:参数的个数$$
:当前进程的PID$!
:上一个后台进程的PID$?
:上一个命令的返回值,0表示成功,非零表示失败
3. 变量赋值方式
显示赋值:
变量名=变量值
#示例 ip=192.168.1.100 school=“Tsinghua University” today1=`date +%F` today2=$(date +%F)
从键盘读入变量值
read
daima#示例: read 变量名 read -p "提示信息:" 变量名 read -t 5 -p "提示信息:" 变量名 read -n 2 变量名
4. 定义或引用变量时注意事项
""
:弱引用#示例 school=“Tsinghua University” echo "${school} is good" #输出:Tsinghua University is good
''
:强引用#示例 school=“Tsinghua University” echo '${school} is good' #输出:${school} is good
``
:命令替换,等价于$(),反引号中的Shell命令会被先执行#示例 touch `data +%F`_file1.txt touch $(date +%F)_file2.txt
5. 变量的运算
5.1. 整数运算
方法一:
expr
#示例 + - \* / % #注意:星号不进行转义会报错 #注意:运算符号左右各有一个空格 expr 1 + 2 expr $num1 + $num2
方法二:
$(())
#示例 + - * / % echo $(($num1+$num2)) #变量名前的$符号可省略不写 echo $((num1+num2)) echo $((5-3*2)) echo $(((5-3)*2)) echo $((2**3)) sum=$((1+2)) ; echo $sum
方法三:
$[]
#示例 + - * / % echo $[5+2] echo $[5**2]
方法四:
let
#示例 let sum=2+3 ; echo $sum let i++ ; echo $i
5.2. 小数运算
#示例
echo "2*4" | bc
echo "2^4" | bc #^代表取幂
echo "scale=2;6/4" | bc #小数点后保留两位
awk 'BEGIN{print 1/2}'
echo "print 5.0/2" | python
5.3. 变量运算实例
计算内存使用量
#!/usr/bin/bash mem_used=`free -m | grep '^Mem:' | awk '{print $3}'` mem_total=`free -m | grep '^Mem:' | awk '{print $2}'` mem_percent=$((mem_used*100/mem_total)) echo "当前内存使用百分比为:$mem_percent %"
ping主机
#!/usr/bin/bash ip=192.168.0.100 i=1 while [ $i -le 5 ] do ping -c1 $ip &>/dev/null if [ $? -eq 0];then echo "$ip is up..." fi let i++ done
6. 变量内容的删除和替换
变量内容删除
#示例 url=www.leeyiding.com echo ${url} #打印变量值(标准查看) #输出:www.leeyiding.com echo ${#url} #查看变量长度 #输出:17 ##字符串从前往后删除 echo ${url#www.} #删除字符串www. #输出:leeyiding.com echo ${url#*.} #删除字符串www.(从前往后,最短匹配) #输出:leeyiding.com echo ${url##*.} #删除字符串www.leeyiding.(从前往后,最长匹配/贪婪匹配) #输出:com ##字符串从后往前删除 echo ${url%.com} #删除字符串.com #输出:www.leeyiding echo ${url%.*} #删除字符串.com(从后往前,最短匹配) #输出:www.leeyiding echo ${url%%.*} #删除字符串.leeyiding.com(从后往前,最长匹配/贪婪匹配) #输出:www
变量内容的索引及切片
#示例 echo ${url:0:13} #从第1个字符开始,共截取13个字符 #输出www.leeyiding echo ${url:4:9} #从第4个字符开始,共截取9个字符 #输出leeyiding echo ${url:4} #从第4个字符开始,一直截取到最后 #输出leeyiding.com
变量内容的替换
#示例 echo ${url/com/cn} #将com替换成cn #输出www.leeyiding.cn echo ${url/w/W} #从前往后将第一个w替换成W #输出Www.leeyiding.com echo ${url//w/W} #贪婪匹配,将所有w替换成W #输出WWW.leeyiding.com
变量的替代
#示例1 unset var1 #取消变量var1 unset var2 unset var3 var2= #赋给变量var2空值 var3=333 #将值333赋给变量var echo ${var1-aaa} #将值aaa赋给变量var1,输出aaa echo ${var2-bbb} #变量var2为空值,无法替换,故输出空值 echo ${var3-ccc} #变量var3已有值,无法替换,故输出333 #总结 #${value-word} #变量没有被赋值:会使用“新的变量值”替代 #变量有被赋值(包括空值):不会被替代
#示例2
unset var1
unset var2
unset var3
var2=
var3=333
echo ${var1:-aaa} #输出aaa
echo ${var2:-bbb} #输出bbb
echo ${var3:-ccc} #输出333
#总结
#${value:-word}
#变量没有被赋值(包括空值):都会使用“新的变量值”替代
#变量有被赋值:不会被替代
#示例3
unset var1
unset var2
unset var3
var2=
var3=333
echo ${var1+aaa} #输出空值
echo ${var2+bbb} #输出bbb
echo ${var3+ccc} #输出ccc
#总结
#${value:+word}
#变量被赋值(包括空值):都会使用“新的变量值”替代
#变量没有被赋值:不会被替代
#示例4
unset var1
unset var2
unset var3
var2=
var3=333
echo ${var1:+aaa} #输出空值
echo ${var2:+bbb} #输出空值
echo ${var3:+ccc} #输出ccc
#总结
#${value:+word}
#变量被赋值:会使用“新的变量值”替代
#变量没有被赋值(包括空值):不会被替代
#示例5
unset var1
unset var2
unset var3
var2=
var3=333
echo ${var1=aaa} #输出aaa
echo ${var2=bbb} #输出空值
echo ${var3=ccc} #输出ccc
#总结
#${value=word}
#变量被赋值或没有赋值:都会使用“新的变量值”替代
#变量为空值:不会被替代
#示例6
unset var1
unset var2
unset var3
var2=
var3=333
echo ${var1:=aaa} #输出aaa
echo ${var2:=bbb} #输出bbb
echo ${var3:=ccc} #输出333
#总结
#${value:=word}
#变量没有被赋值(包括空值):都会使用“新的变量值”替代
#变量被赋值:不会被替代
#示例7
unset var1
unset var2
unset var3
var2=
var3=333
echo ${var1?aaa} #报错,输出 bash: var1: aaa
echo ${var2?bbb} #输出空值
echo ${var3?ccc} #输出333
#总结
#${value?word}
#变量没有被赋值:报错
#变量被赋值(包括空值):不会被替代
#示例8
unset var1
unset var2
unset var3
var2=
var3=333
echo ${var1:?aaa} #报错,输出 bash: var1: aaa
echo ${var2:?bbb} #报错,输出 bash: var2: bbb
echo ${var3:?ccc} #输出333
#总结
#${value:?word}
#变量没有被赋值(包括空值):报错
#变量被赋值:不会被替代
7. i++ 和++i
对变量值的影响
#示例 i=1 let i++ echo $i #输出2 j=1 let ++j echo $j #输出2
对表达式的值的影响
#示例 unset i unset j i=1 j=1 let x=i++ #先赋值,再运算 let y=++j #先运算,再赋值 echo $i #输出2 echo $j #输出2 echo $x #输出1 echo $y #输出2
8. 基础总结
8.1. 各种符号
()
:在子Shell中执行命令(())
:数值比较、运算$()
:命令替换,等用于``
$(())
:整数运算{}
:集合${}
:变量的引用、内容替换或替代[]
:条件测试(文件测试、数值比较、字符串比较)[[]]
:在[]
基础上支持正则表达式$[]
:整数运算
8.2. 执行脚本
执行方式 | 权限 | 执行Shell |
---|---|---|
./01.sh | 需要执行权限 | 在子Shell中执行 |
bash 01.sh | 不需要执行权限 | 在子Shell中执行 |
. 01.sh | 不需要执行权限 | 在当前Shell中执行 |
source 01.sh | 不需要执行权限 | 在当前Shell中执行 |
8.3 调试脚本
sh -n 02.sh
:仅调试syntax errorsh -vx 0.2sh
:以调试的方式执行,查询整个执行过程
四. Shell 条件测试
1. 表达式
test 条件表达式
[ 条件表达式 ]
[[ 条件表达式 ]]
C语言风格
(())
((1>2))
((1==2))
((1>=2))
((1!=2))
((`id -u`>0))
(($UID==0))
2. 条件测试参数
表达式 | 描述 | 测试类型 |
---|---|---|
(EXPRESSION) | 表达式为真 | 条件判断 |
! EXPRESSION | 表达式为假 | 条件判断 |
EXPRESSION1 -a EXPRESSION2 | 表达式1和表达式2都为真 | 条件判断 |
EXPRESSION1 -o EXPRESSION2 | 表达式1或表达式2其中一个为真 | 条件判断 |
-n STRING | 字符串长度不为0 | 字符串判断 |
-z STRING | 字符串长度为0 | 字符串判断 |
STRING1 = STRING2 | 字符串1和字符串2相同 | 字符串判断 |
STRING1 != STRING2 | 字符串1和字符串2不相同 | 字符串判断 |
INTEGER1 -eq INTEGER2 | 整数1与整数2相同 | 整数判断 |
INTEGER1 -ge INTEGER2 | 整数1大于等于整数2 | 整数判断 |
INTEGER1 -gt INTEGER2 | 整数1大于整数2 | 整数判断 |
INTEGER1 -le INTEGER2 | 整数1小于等于整数2 | 整数判断 |
INTEGER1 -lt INTEGER2 | 整数1小于整数2 | 整数判断 |
INTEGER1 -ne INTEGER2 | 整数1不等于整数2 | 整数判断 |
-d DIR | 检查目录是否存在 | 文件判断 |
-e FILE or DIR | 检查文件或目录是否存在 | 文件判断 |
-f FILE | 检查文件是否存在 | 文件判断 |
-r FILE | 检查文件是否存在并可读 | 文件判断 |
-s FILE | 检查文件是否存在并非空 | 文件判断 |
-w FILE | 检查文件是否存在并可写 | 文件判断 |
-x FILE | 检查文件是否存在并可执行 | 文件判断 |
-O FILE | 检查文件是否存在并属当前用户所有 | 文件判断 |
-G FILE | 检查文件是否存在并且默认组与当前用户相同 | 文件判断 |
FILE1 -nt FILE2 | 检查文件1 是否比文件2 新 | 文件判断 |
FILE1 -ot FILE2 | 检查文件1 是否比文件2 旧 | 文件判断 |
3. 示例
#判断目录是否存在
test -d /home
echo $? #目录存在,输出0
test -d /home111
echo $? #目录不存在,输出1
#逻辑判断
[ 1 -lt 2 -a 5 -gt 10 ]; echo$? #输出1
[ 1 -lt 2 -o 5 -gt 10 ]; echo$? #输出0
[[ 1 -lt && 5 -gt 10 ]]; echo$? #输出1
[[ 1 -lt || 5 -gt 10 ]]; echo$? #输出0
#!/usr/bin/bash
#创建用户脚本 creat_user.sh
read -p "Please input a username:" user
if id $user &>/dev/null; then
echo "user $user already exists"
else
useradd $user
if [ $? -eq 0 ]; then
echo "$user is created."
fi
fi
#!/usr/bin/bash
#根分区磁盘用量报警 disk_use.sh
disk_use=`df -Th | grep '/$' | awk '{print $(NF-1)}' | awk -F "%" '{print $1}'`
mail_user=root
if [ $disk_use -ge 90 ]; then
echo "`data +%F-%H` disk:${disk_use}% is used" |mail -s "disk war..." $mail_user
fi
#!/usr/bin/bash
#批量添加用户 useradd1.sh
read -p "Please input number:" num
read -p "Please input prefix:" prefix
for i in `seq $num` #seq创建从1到num序列
do
user=$prefix$i
useradd $user
echo "123" | passwd --stdin $user &>/dev/null
if [ $? -eq 0 ];then
echo "$user is created"
fi
done
#!/usr/bin/bash
#批量添加用户(严格模式) useradd2.sh
read -p "Please input number:" num
if [[ ! "$num" =~ ^[0-9]+$ || "$num" =~ ^0+$ ]];then #正则匹配,判断$num是否是数字或为一个或多个0
echo "Error number"
exit
fi
read -p "Please input prefix:" prefix
if [ -z "$prefix" ];then #判断字符串是否为0
echo "Error prefix"
exit
fi
for i in `seq $num` #seq创建从1到num序列
do
user=$prefix$i
useradd $user
echo "123" | passwd --stdin $user &>/dev/null
if [ $? -eq 0 ];then
echo "$user is created"
fi
done
#/usr/bin/local
#判断是否是命令 if_commanf.sh
command=/bin/date
if command -v $command &>/dev/null;then
: #不执行任何命令,等同于true
else
yum install $command
fi
五. 匹配模式:case
1. case语法结构
case $Var in
Pattern1)
Command1
;;
Pattern2)
Command2
;;
Patter
Command3
;;
*)
NoPatternCommand
esac
2. 示例
#!/usr/bin/bash
#多系统配置yum源 yum_config_case.sh
yum_server=192.168.0.100
os_version=`cat /etc/redhat-release | awk '{print $4}' | awk -F "." '{print $1"."$2}'`
[ -d /etc/yum.repos.d] || mkdri /etc/yum.repos.d/bak
mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/bak &>/dev/null
case "$os_version" in
"7.3")
cat > /etc/yum.repos.d/centos7u3.repo <<-EOF
[centos7u3]
name=centos7u3
baseurl=ftp://$yum_server/centos7u3
gpgcheck=0
EOF
echo "7.3 yum configure..."
;;
"6.8")
curl -o /etc/yum.repos.d/centos6u8.repo ftp://$yum_server/centos6u8.repo
;;
"5.9")
curl -o /etc/yum.repos.d/Centos-Base.repo http://mirrors.aliyun.com/repo/Centos-5.repo
;;
*)
echo "error"
esac
echo "Finish"
#!/usr/bin/bash
#删除用户 del_user.sh
read -p "Please input a username:" user
id $user &>/dev/null
if [ $? -ne 0 ];then
echo "No user named $user"
exit 1
fi
read -p "Are you sure you want delete $user ?[y/n]" action
#if[ "$action" = "y" -o "$action"= "Y" -o "$action" = "yes" -o "$action" = "YES" ];then
# userdel -r $user
# echo "$user is deleted"
#fi
case "$action" in
y|Y|yes|YES)
userdel -r $user
echo "$user is deleted"
;;
*)
echo "error"
esac
#!/usr/bin/bash
#简易JumpServer跳板机系统 jump_server.sh
#创建管理用户jms
#useradd jms
#创建密钥
#ssh-keygen
#拷贝密钥
#ssh-copy-id 192.168.0.101
#ssh-copy-id 192.168.0.102
#ssh-copy-id 192.168.0.103
#脚本添加进.bash——profile
#/home/jms/jump_server.sh
trap "" HUP INT OUIT TSTP #捕捉信号,禁止退出
web1=192.168.0.101
web2=192.168.0.102
mysql1=192.168.0.103
clear
while:
do
cat << -EOF
+-----------------------------+
| Jump_server |
| 1. web1 |
| 2. web2 |
| 3. mysql1 |
+-----------------------------+
EOF
echo -en "\e[1;32input number: \e[0m"
read num
case "$num" in
1)
ssh jms@$web1
;;
2)
ssh jms@$web2
;;
3)
ssh jms@$mysql1
;;
"")
;;
*)
echo "error"
esac
done
#!/usr/bin/bash
#简易系统工具箱 system_manage.sh
menu() {
cat <<-EOF
#################################
# h. help #
# f. disk partition #
# d. filesystem mount #
# u. system load #
# q. exit #
#################################
EOF
}
menu
while true
do
read -p "Please input[h for help]: " action
case "$action" in
h) clear; menu;;
f) fdisk -l;;
d) df -Th;;
m) free -m;;
u) uptime;;
q) break;;
"") ;;
*) echo "error"
esac
done
六. 流程控制:if
1. 结构
#单分支结构
if 条件测试
then 命令序列
fi
#双分支结构
if 条件测试
then 命令序列
else 命令序列
fi
#多分支结构
if 条件测试1
then 命令序列1
elif 条件测试2
then 命令序列
elif 条件测试3
then 命令序列
else 命令序列
fi
2. 示例
#!/usr/bin/bash
#安装Apache install_apache01.sh
ping c1 wwww.baidu.com &>/dev/null
if [ $? -ne 0 ];then
echo "connect unreachable"
exit
fi
yum -y install httpd
systemstl start httpd
systemstl enable httpd
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
sed -ri '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config
setenforce 0
#!/usr/bin/bash
#安装Apache install_apache02.sh
gatewau=192.168.0.1
ping c1 wwww.baidu.com &>/dev/null
if [ $? -eq 0 ];then
yum -y install httpd
systemstl start httpd
systemstl enable httpd
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
sed -ri '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config
setenforce 0
#测试
curl http://127.0.0.1 &>/dev/null
if [ $? eq 0 ];then
echo "Apache is OK!"
fi
elif ping -c1 $gateway &>/dev/null;then
echo "Check DNS..."
else
echo "check ip address!"
fi
#!/usr/bin/bash
#多系统配置yum源 yum_config.sh
os_version=`cat /etc/redhat-release | awk '{print $4}' | awk -F "." '{print $1"."$2}'`
yum_server=192.168.0.100
[ -d /etc/yum.repos.d] || mkdri /etc/yum.repos.d/bak
mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/bak &>/dev/null
if ["$os_version" = "7.3" ];then
cat > /etc/yum.repos.d/centos7u3.repo <<-EOF
[centos7u3]
name=centos7u3
baseurl=ftp://$yum_server/centos7u3
gpgcheck=0
EOF
echo "7.3 yum configure..."
elif ["$os_version" = "6.8" ];then
curl -o /etc/yum.repos.d/centos6u8.repo ftp://$yum_server/centos6u8.repo
elif ["$os_version" = "5.9" ];then
curl -o /etc/yum.repos.d/Centos-Base.repo http://mirrors.aliyun.com/repo/Centos-5.repo
fi
七. Shell 循环:for
1. 语法结构
for 变量名 in 取值列表
do
循环体
done
2. 示例
#!/usr/bin/bash
#探测局域网主机
>ip.txt
for i in {2..254}
do
{
ip=192.168.0.$i
ping -c1 -W1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip" | tee -a ip.txt
fi
}&
done
wait
echo "finishi...."
#!/usr/bin/bash
#批量创建用户
while true
do
read -p "Please enter prefix & pass & num: " prefix pass num
printf"user information:
----------------------
user prefix: $prefix
user password: $pass
user number: $num
----------------------
"
read -p "Are you sure?[y/n]" 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 "user $user already exists"
else
useradd $user
echo "pass" | passwd --stdin $user &>/dev/null
if [ $? -eq 0 ];then
echo "$user is created."
fi
di
done
#!/usr/bin/bash
#批量从文件读入用户名,创建用户
pass=123
if [ $# -eq 0 ];then
echo "usage: 'basename $0' file"
exit 1
fi
if [ ! -f $1 ];then
echo "error file"
exit 2
fi
for user in `cat $1`
do
id $user &>/dev/null
if [ $? -eq 0 ];then
echo "user $user already exists"
else
useradd $user
echo "$pass" | passwd --stdin $user &>/dev/null
if [ $? -eq 0 ] ;then
echo "$user is created."
fi
fi
done
#!/usr/bin/bash
#批量从文件读入用户名和密码,创建用户
if [ $# -eq 0 ];then
echo "usage: 'basename $0' file"
exit 1
fi
if [ ! -f $1 ];then
echo "error file"
exit 2
fi
#重新定义分割符
IFS=$"\n"
for line in `cat $1`
do
if [ ${#line} -eq 0 ];then
continue
fi
user=`echo "$line" | awk '{print $1}'`
pass=`echo "$line" | awk '{print $2}'`
id $user &>/dev/null
if [ $? -eq 0 ];then
echo "user $user already exists"
else
useradd $user
echo "$pass" | passwd --stdin $user &>/dev/null
if [ $? -eq 0 ] ;then
echo "$user is created."
fi
fi
done
#!/usr/bin/bash
#批量修改密码
read -p "Please enter a New Password: " pass
for ip in $(cat ip.txt)
do
{
ping -c1 -W1 $ip &>/dev/null
if [ $? eq 0 ];then
ssh $ip "echo $pass | passwd --stdin root"
if [ $? -eq 0 ];then
echo "$ip" >> ok_`data +%F`.txt
else
echo "$ip" >> fail_`data +%F`.txt
fi
else
echo "$ip" >> fail_`data +%F`.txt
fi
}&
done
wait
echo "Everything is OK"
#!/usr/bin/bash
# 批量修改远程主机配置文件
for ip 'cat ip.txt'
do
{
ping -c1 -W1 $ip &>/dev/null
if [ $? -eq 0 ];then
ssh $ip "sed -ri '/^#UseDNS/cUseDNS no' /etc/ssh/sshd_config"
ssh $ip "systemctl stop firewalld; systemctl disable firewalled"
fi
}&
done
wait
八. Shell循环:while until
1. 语法结构
#while
#当条件测试成立(条件测试为真),执行循环体
while 条件测试
do
循环体
done
#until
#当条件测试成立(条件测试为假),执行循环体
until 条件测试
do
循环体
done
2. 示例
#!/usr/bin/bash
#while循环读取文件用户名,批量创建用户
while read user
do
id $user &>/dev/null
if [ $? -eq 0 ] ;then
echo "user $user already exista"
else
useradd $user
if [ $? -eq 0 ];then
echo "$user is created."
fi
fi
done < user.txt
#!/usr/bin/bash
#读取文件用户名和密码,批量创建用户
while read line
do
if [ ${#line} -eq 0];then
continue
fi
user=`echo "$line" | awk '{print $1}'`
pass=`echo "$line" | awk '{print $2}'`
id $user &>/dev/null
if [ $? -eq 0 ] ;then
echo "user $user already exista"
else
useradd $user
echo "$pass" | passwd --stdin $user &>/dev/null
if [ $? -eq 0 ];then
echo "$user is created."
fi
fi
done < user.txt
#!/usr/bin/bash
#while测试远程主机连接
ip=192.168.0.100
while ping -c1 -W1 $ip &>/dev/null
do
sleep 1
done
echo "$ip is down!"
#!/usr/bin/bash
#until测试远程主机连接
ip=192.168.0.100
until ping -c1 -W1 $ip &>/dev/null
do
sleep 1
done
echo "$ip is up!"
九. Shell 并发控制
1. 传统并发方式
在循环体中套用{}&
可以将每个循环放入后台执行,达到处理并发的目的,但只是用于小规模的并发,且该并发是无控制的,无限制蔓延,导致每个并发的完成没有先后。
#!/usr/bin/bash
#测试远程主机连通
for i in {1..254}
do
{
ip=192.168.0.$i
ping c1 -W1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip is up"
else
echo "$ip is down"
fi
}&
done
wait
2. fd和命名管道实现并发控制
2.1. 句柄概念
File Descriptors(FD,文件描述符或文件句柄)
ls /proc/$$/fd
#0 1 10 2 29
ll /proc/$$/fd
#总用量 0
#lrwx------ 1 root root 64 2月 29 17:24 0 -> /dev/pts/3
#lrwx------ 1 root rootd 64 2月 29 17:24 1 -> /dev/pts/3
#lrwx------ 1 root root 64 2月 29 17:24 10 -> /dev/pts/3
#lrwx------ 1 root root 64 2月 29 17:24 2 -> /dev/pts/3
touch file1
exec 6<> file1 #创建句柄
ll /proc/$$/fd
#总用量 0
#lrwx------ 1 root root 64 2月 29 17:32 0 -> /dev/pts/3
#lrwx------ 1 root rootd 64 2月 29 17:32 1 -> /dev/pts/3
#lrwx------ 1 root root 64 2月 29 17:32 10 -> /dev/pts/3
#lrwx------ 1 root root 64 2月 29 17:32 2 -> /dev/pts/3
#lrwx------ 1 root root 64 2月 29 17:32 6 -> /root/file1ls
echo "111" >> /proc/$$/fd/6
cat file1
#111
rm -rf /file1
ll /proc/$$/fd
#总用量 0
#lrwx------ 1 root root 64 2月 29 17:33 0 -> /dev/pts/3
#lrwx------ 1 root rootd 64 2月 29 17:33 1 -> /dev/pts/3
#lrwx------ 1 root root 64 2月 29 17:33 10 -> /dev/pts/3
#lrwx------ 1 root root 64 2月 29 17:33 2 -> /dev/pts/3
#lrwx------ 1 root root 64 2月 29 17:33 6 -> /root/file1 (deleted)
cp /proc/$$/fd/6 file1
cat file1
#1111
exec 6<&- #删除句柄
ll /proc/$$/fd
#总用量 0
#lrwx------ 1 root root 64 2月 29 17:34 0 -> /dev/pts/3
#lrwx------ 1 root rootd 64 2月 29 17:34 1 -> /dev/pts/3
#lrwx------ 1 root root 64 2月 29 17:34 10 -> /dev/pts/3
#lrwx------ 1 root root 64 2月 29 17:34 2 -> /dev/pts/3
2.2. 管道
匿名管道
rpm -qa | grep bash
命名管道
#使用命名管道,将内容传给另一终端 mkfifo /tmp/fifo1 file /tmp/fifo1 #/tmp/fifo1: fifo (named pipe) #终端1 tty #/dev/pts/0 ls /dev > /tmp/fifo1 #终端2 tty #/dev/pts/1 grep 'sda' /tmp/fifo1 # sda sda1 sda2 sda3
2.3. 举例
#!/usr/bin/bash
#测试远程主机连通
thread=5
tmp_fifofile=/tmp/$$.fifo
mkfifo $tmp_fifofile #创建管道文件
exec 8<> $tmp_fifofile #以编号为8的文件句柄打开管道文件
rm -f $tmp_fifofile #删除管道文件
for i in `seq $thread`
do
echo >&8
done
for i in {1..254}
do
read -u 8
#读到编号为8的文件句柄执行本次循环,否则等待之前循环完成后
#再执行本次循环,最大并发数量为变量thread的值
{
ip=192.168.0.$i
ping c1 -W1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip is up"
else
echo "$ip is down"
fi
echo >&8 #代表本次循环执行完成,释放进程
}&
done
wait
exec 8>&- #释放文件句柄
十. Expect 处理交互命令
1. 介绍
借助expect可以处理交互式的命令,使之自动化完成
2. 安装
yum -y install expect
3. 语法结构
expect [option] [args]
参数
- -c 从命令行执行expect脚本
- -d 输出调试信息
相关命令
- spawn:启动新的进程
- send:用于向进程发送字符串
- expect:从进程接收字符串
- interact:允许用户交互
- exp_contimue:匹配多个字符串在执行动作后的命令
4. 示例
#!/usr/bin/expect
#ssh非交互式连接远程主机
set ip [lindex $argv 0] #接收第一个参数
#set ip 192.168.0.100
set user root
set password centos
set timeout 5
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\r" ; exp_continue }
"password:" { send "$password\r"}
}
expect "#"
send "useradd 123\r"
send "pwd\r"
send "exit\r"
expect eof #结束交互
#!/usr/bin/expect
#scp非交互式传输文件
set ip [lindex $argv 0]
set user root
set password centos
set timeout 5
spawn scp -r /etc/hosts $user@$ip:/tmp
expect {
"yes/no" { send "yes\r" ; exp_continue }
"password:" { send "$password\r"}
}
expect eof
#!/usr/bin/bash
#批量推送公钥
>ip.txt
passwd=centos
#判断是否安装expect
rpm -q expect &>/dev/null
if [ &? -ne 0 ];then
yum -y install expect &>/dev/null
echo "expect is installed"
fi
#判断是否安装密钥
if [ ! -f ~/.ssh/id_rsa ];then
ssh-keygen -P "" -f ~/.ssh/id_rsa #非交互式创建密钥
fi
for i in {2..254}
do
{
ip=192.168.0.$i
ping -c1 -W1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip" >> ip.txt
/usr/bin/expect <<-EOF
set timeout 10
spawn ssh-copy-id $ip
expect {
"yes/no" { send "yes\r" ; exp_continue }
"password:" { send "$password" }
}
expect eof
EOF
fi
}&
done
wait
echo "Finish....."
十一. Shell数组变量
1. 普通数组
1.1. 概念
普通数组:只能使用整数作为数组索引
1.2. 定义数组
#方法一:一次赋一个值
数组名[下标]=变量值
array1[0]=pear
array1[1]=apple
array1[2]=orange
array1[2]=peach
#方法二:一次赋多个值
数组名=(变量1 变量2 变量3)
array2=(tom jack alice)
array3=(`cat /etc/passwd`)
array4=(`ls /var/ftp/Shell/for8`)
array5=(tom jack alice "bash shell")
colors=($red $blue $green $recolor)
array5=(1 2 3 4 5 6 7 "linux shell" [20]=puppet)
1.3. 查看数组
declare -a
#declare -a array1='([0]="pear" [1]="apple" [2]="orange" [3]="peach")'
#declare -a array2='([0]="tom" [1]="jack" [2]="alice")'
1.4. 访问数组元素
echo ${array1[0]} #访问数组中第一个元素
echo ${array1[@]} #访问数组中所有元素
echo ${array1[*]} #访问数组中所有元素
echo ${#array1[@]} #统计数组元素的个数
echo ${!array1[@]} #获取数组元素的索引
echo ${array1[@]:1} #从数组下标1开始
echo ${array1[@]:1:2} #从数组下标1开始,访问两个元素
1.5. 遍历数组
通过数组元素的个数遍历
通过数组元素的索引进行遍历
1.6. 示例
#!/usr/bin/bash
#使用while遍历hosts文件
while
do
hosts[++i]=$line
done </etc/hosts
for i in ${!hosts[@]}
do
echo "$i: ${host[$i]}"
done
#!/usr/bin/bash
#使用for遍历hosts文件
OLD_IFS=$IFS
IFS=$'\n'
for line in 'cat /etc/hosts'
do
hosts[++1]=$line
done
for i in ${!hosts[@]}
do
echo "$i: ${host[$i]}"
done
IFS=$OLD_IFS
2. 关联数组
2.1. 概念
普通数组:可以使用字符串作为数组索引
2.2. 定义关联数组
#声明关联数组变量
declare -A ass_array1
declare -A ass_array2
#方法一:一次赋一个值
数组名[下标]=变量值
ass_array1[index1]=pear
ass_array1[index2]=apple
ass_array1[index3]=orange
ass_array1[index4]=peach
#方法二:一次赋多个值
数组名=([索引1]=变量1 [索引2]=变量2 [索引3]=变量3)
array2=([name]=jack [age]=10 [sex]=male [height]=160)
2.3. 示例
#!/usr/bin/bash
#统计性别
declare -A sex
while
do
type=`echo $line | awk '{print $2}'`
let sex[$type]++
done < sec.txt
for i in ${!sex[@]}
do
echo "$i: $sex[$i]"
done
#!/usr/bin/bash
#统计不同shell数量
declare -A shells
while read line
do
type=`echo $line | awk -F ":" '{print $NF}'`
let shells[$type]++
done < /etc/passwd
for i in ${!sheels[@]}
do
echo "$i: ${shells[$i]}"
done
#!/usr/bin/bash
#统计TCP连接状态
while :
do
declare -A status
unset satus
type=`ss -an | grep :80 | awk '{print $2}'`
for i in $type
do
let status[$i]++
done
for j in ${!status[@]}
do
echo "$j: ${status[$j]}"
done
sleep 1
clear
done
十二. function函数的定义及调用
1. 函数的概念
完成特定功能的代码片段
在Shell中定义函数可以使用代码模块化,便于复用代码
函数必须先定义才能使用
- 传参
$1
、$2
- 变量 local
- 返回值
return $?
2. 定义函数
#方法一
函数名(){
函数要实现的代码
}
#方法二
function 函数名(){
函数要实现的代码
}
3. 调用函数
函数名
函数名 参数1 参数2
4. 示例
#!/usr/bin/bash
#阶乘
function factorial() {
factorial=1
for((i=1;i<=$1;i++))
do
factorial=$[ $factorial * $i ]
done
echo "$1的阶乘是: $factorial"
}
factorial $1
#!/usr/bin/bash
#函数使用return输出
fun2() {
read -p "enter num: " num
return $[2*$num] #最大不超过255
}
fun2
echo "fun 2 return value is : $?"
#!/usr/bin/bash
#函数使用out输出
fun2() {
read -p "enter num: " num
echo $[2*$num]
}
result=`fun2` #函数执行结果返回给变量
echo "fun 2 return value is : $return"
#!/usr/bin/bash
#函数位置参数
if [ $# -ne 3 ];then
echo "usage: `basename $0` par1 par2 par3"
fi
fun3() {
echo "$(($1 * $2 * $3))" #$1 $2 $3为函数位置参数
}
result=`fun3 $1 $2 $3` #$1 $2 $3为脚本位置参数
echo "result is: $result"
#!/usr/bin/bash
#函数使用数组传参
num=(1 2 3 4 5)
#echo "${num[@]}"
array() {
local factorial=1 #局部变量,函数内部生效
for i in $* #传入所有位置参数
do
factorial=$[$factorial * $i]
done
echo "$factorial"
}
array ${num[*]}
#!/usr/bin/bash
#函数输出数组变量
num=(1 2 3)
array() {
local newarray=($*)
local i
for ((i=0;i<$#;i++))
do
newarray[$i]=$(( ${newarray[$i]} * 5 ))
done
echo "${newarray[*]}"
}
result=`array ${num[*]}`
echo ${result[*]}
#!/usr/bin/bash
#函数输出数组变量2
num=(1 2 3)
array() {
local i
local j
local outarray=()
for i in $*
do
newarray[j++]=$[$i*5]
done
echo "${outarray[*]}"
}
result=`array ${num[*]}`
echo ${result[*]}
十三. Shell程序内置命令
1. 概述
:
true
false
exit
:退出整个程序break
:结束当前循环或跳出本层循环continue
:忽略本次循环剩余的代码,直接进行下一次循环shift
:使位置参数向左移动,默认移动1位,可以使用shift 2
2. 示例
#!/usr/bin/bash
for i in {A..D}
do
echo -n "$i"
for j in {1..9}
do
if [ $j -eq 5];then
continue
fi
echo -n $j
done
echo
done
#!/usr/bin/bash
while [ $# -ne 0]
do
let sum+=$1
shift
done
echo "sum: $sum"
版权属于:LeeYD · Blog
本文标题:Shell 编程入门笔记
本文链接:https://www.leeyiding.com/archives/44/
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 4.0 许可协议
若转载本文,请标明出处并告知本人
【阿里云】爆款云产品,新客特惠全年最低价,云服务器低至0.4折起,11.1开售
【腾讯云】爆款1核2G云服务器首年48元,还有iPad Pro、Bose耳机、京东卡等你来抽!
【华为云】上云特惠巨划算,免单抽奖享豪礼
【七牛云】爆款云产品全年最低价,热门产品 0 元秒杀,参与抽奖赢新款 iPhone
写得蛮好的,请问可以转载吗
滴!访客卡!请上车的乘客系好安全带,现在是:Tue Mar 03 2020 12:32:45 GMT+0800 (中国标准时间)
滴!访客卡!请上车的乘客系好安全带,现在是:Tue Mar 03 2020 12:32:47 GMT+0800 (中国标准时间)
你好,可以转载,请问评论邮箱是你常用邮箱吗,我将markdown原文发送给你。