shell编程

Posted opesn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell编程相关的知识,希望对你有一定的参考价值。

shell编程

shell编程能做什么

1)安装操作系统(手动安全ISO) 自动化安装(kickstart cobbler)shell脚本
2)初始化操作系统 优化(SSH优化、关闭SElinux、优化防火墙、NTP时间同步、更改默认YUM、源字符集、安装常用的软件 lrzsz net-tools tree.. wget、隐藏版本信息、加大文件描述符、内核优化...)初始化写入脚本 自动化执行
3) 安装服务(nginx php mysql MariaDB NFS sersync REDIS keepalived docker....zabbix)使用shell脚本自动选择安装
4) 启动----停止(centos6.x: /etc/init.d/server start;centos7.x:systemctl start server) 系统默认的启动方式 shell脚本
5) 监控 zabbix、cacti、nagios、ELK、公司研发的监控平台;监控的值(shell脚本统计)
6) 日志统计 三剑客 日志切割(脚本+定时任务)

初识Shell

什么是shell

pwd ls 都是通过bash解释的、Shell是一个命令解释器,作用是解释执行用户输入的命令及程序
交互式模式就是shell等待你的输入,并且执行你提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、签退。当你签退后,shell也终止了。shell也可以运行在另外一种模式:非交互式模式。在这种模式下,shell不与你进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾,shell也就终止了。

什么是shell脚本

命令的大礼包 把可执行命令放到文件中 称为shell脚本(判断语句 循环语句 数组....)
编译语言: 把代码编程成二进制语言 机器可识别 一次编译 终身运行 速度快
脚本语言: 每次执行脚本 一行行的解释

shell的书写规范

1) shell脚本存放统一的目录
2) 脚本名字的结尾使用.sh
3) 脚本的开头 必须有解释器 #!/bin/bash
4) 脚本内有作者信息 脚本信息
5) 每段代码块有注释(尽量使用英文)
6) 标点符号 (语法尽量一次性书写完毕)
7) 成对的符号 一次性书写完毕

第一个shell脚本

[root@test scripts]# vim hello.sh
#!/bin/bash
echo "Hello Word!"
[root@test scripts]# 
 #执行脚本的三种方法
 #方法1 直接使用解释器执行
[root@test scripts]# bash hello.sh 
Hello Word!
[root@test scripts]# 
#方法2 全路径方式执行 需要执行权限
[root@test scripts]# chmod +x hello.sh 
[root@test scripts]# ./hello.sh 
Hello Word!
[root@test scripts]# 
#方法3 source .执行脚本  子shell中的内容调用到父shell中执行
[root@test scripts]# source hello.sh 
Hello Word!
[root@test scripts]# . hello.sh 
Hello Word!
[root@test scripts]# 

环境变量

什么是环境变量?

LANG=‘en_US.UTF-8‘
name=test
右边一堆内容 使用一个名字来代替称为环境变量
如何查看变量环境变量 加$
echo $name
env 查看系统定义的环境变量

变量的分类

全局变量(环境变量) 针对系统所有用户生效
局部变量(普通变量) 针对当前登录用户生效

变量生存周期分类 两类

临时性 export 声明变量即可 或者 name=oldboy
永久性 修改配置文件 /etc/profile

#环境变量文件执行顺序
1. /etc/profile
2. .bash_profile
3. .bashrc
4. /etc/bashrc
#如果按照文件内容的生效顺序
1. /etc/profile
2. .bashrc
3. /etc/bashrc
4. .bash_profile

如何定义环境变量

要求以字母 数字 和下划线组合  尽量以字母和_开头 等号两端不允许有空格 名字 见名知其意
变量名字的定义的语法:
OLDBOY_AGE=18  系统用的变量都是大写
oldboy_age=18  全小写
oldboy_Age=18  小驼峰语法
Oldboy_Age=18  大驼峰语法
变量值得定义:
1.数字定义  连续的数字    test=188888
2.字符串定义 连续的字符串 name="test test test" 不知道加什么就加双引号
例子: CODE_DIR=/etc/sysconfig/network-scripts/
[root@test scripts]# name=opesn
[root@test scripts]# echo "$name"
opesn
[root@test scripts]# echo '$name'
$name
[root@test scripts]# 
3.命令的定义
方法1 [root@test scripts]# tim=`date +%F`
方法2 [root@test scripts]# tim=$(date +%F)

特殊的位置变量

$0 代表了脚本的名称,如果使用全路径执行则脚本名称带全路径
$n 代表脚本的第n个参数 $0被脚本名称占用 参数从$1开始 $9 以后需要加
$# 代表脚本传参的总个数
$* 获取脚本的所有的参数 不加双引号和$@相同 加上双引号则把参数视为一个整体 $1$2$3(在循环体中)
$@ 获取脚本的所有的参数 不加双引号和$* 相同 加上双引号则把参数视为独立的(在循环体中)
$? 获取上一条命令的结果 0为成功 非0失败 0-255之间
$$ 获取脚本的PID
$! 上一个在后台运行的脚本的PID 调试使用
$_ 获取命令行或脚本的最后一个参数 相当于ESC .

变量传参的三种方法

方法1 直接传参
方法2 赋值传参
方法3 read传参
#方法1
[root@test scripts]# vim test.sh
#!/bin/bash
echo $1 $2
[root@test scripts]# bash test.sh 1 2
1 2
[root@test scripts]# 
#方法2
[root@test scripts]# vim test.sh
#!/bin/bash
name1=$1
name2=$2
echo $name1 $name2                
[root@test scripts]# bash test.sh 1 2
1 2
[root@test scripts]#
#方法3
[root@test scripts]# bash test.sh
please input name : 1
please input name : 2
1
2
[root@test scripts]# 

#使用read方式更改主机名和IP地址
[root@test scripts]# vim hostname_and_ip.sh
#!/bin/bash
eth=/etc/sysconfig/network-scripts/ifcfg-eth0
read -p "please input hostname: " name
hostnamectl set-hostname $name
read -p "please input IP: " ipadd
sed -i "/IPADDR/c IPADDR=$ipadd" $eth                     
[root@test scripts]# 
[root@test scripts]# bash hostname_and_ip.sh 
please input hostname: opesn
please input IP: 10.0.1.250         
[root@test scripts]# 

变量的子串

#计算变量的长度
[root@test scripts]# echo $test|wc -L
10
[root@test scripts]# echo $#test
10
[root@test scripts]# expr length "$test"
10
[root@test scripts]# echo $test |awk 'print length'
10
[root@test scripts]# 
#截取变量中的字符
[root@test scripts]# echo $test:2:2
am
[root@test scripts]# echo $test:2
am opesn
[root@test scripts]# echo $test:2:4
am o
[root@test scripts]# 

变量子串的删除和替换

#从前面往后面删除(##代表贪婪匹配)
[root@test scripts]# url=www.baidu.com
[root@test scripts]# echo $url
www.baidu.com
[root@test scripts]# echo $url#*.
baidu.com
[root@test scripts]# echo $url#*.*.
com
[root@test scripts]# echo $url##*.
com
[root@test scripts]# 

#从后面往前面删除(%代表贪婪匹配)
[root@test scripts]# url=www.baidu.com
[root@test scripts]# echo $url
www.baidu.com
[root@test scripts]# echo $url%.*
www.baidu
[root@test scripts]# echo $url%.*.*
www
[root@test scripts]# echo $url%%.*
www
[root@test scripts]# 

#替换(//代表贪婪匹配)
[root@test scripts]# url=www.baidu.com
[root@test scripts]# echo $url
www.baidu.com
[root@test scripts]# echo $url/baidu/qq
www.qq.com
[root@test scripts]# echo $url/w/a
aww.baidu.com
[root@test scripts]# echo $url//w/a
aaa.baidu.com
[root@test scripts]# 

数值运算

#expr只能做整数运算
[root@test scripts]# expr 1 + 1
2
[root@test scripts]# expr 5 - 5
0
[root@test scripts]# expr 1 \* 5
5
[root@test scripts]# expr 10 / 5
2
[root@test scripts]# 

#$[]只能做整数运算
[root@test scripts]# echo $[10+10]
20
[root@test scripts]# echo $[10-10]
0
[root@test scripts]# echo $[10*10]
100
[root@test scripts]# echo $[10/10]
1
[root@test scripts]# 

#$(())只能做整数运算,效率最高的运算
[root@test scripts]# echo $((5+5))
10
[root@test scripts]# echo $((5-5))
0
[root@test scripts]# echo $((5*5))
25
[root@test scripts]# echo $((5/5))
1
[root@test scripts]# 

#let只能做整数运算
[root@test scripts]# let sum=1+1
[root@test scripts]# echo $sum
2
[root@test scripts]# 

#bc整数、小数运算
[root@test scripts]# echo 10+10|bc
20
[root@test scripts]# echo 10-10|bc
0
[root@test scripts]# echo 10*10|bc
100
[root@test scripts]# echo 10/10|bc
1
[root@test scripts]# 

#awk小数、整数运算
[root@test scripts]# awk 'BEGINprint 10-5.5'
4.5
[root@test scripts]# awk 'BEGINprint 10+5.5'
15.5
[root@test scripts]# awk 'BEGINprint 10*5'
50
[root@test scripts]# awk 'BEGINprint 10/5'
2
[root@test scripts]# 

#python小数、整数运算
[root@test scripts]# python
>>> 10+5
15
>>> 10-2
8
>>> 5*5
25
>>> 5/5
1
>>> 

#expr 判断传参是否为整数
[root@test ~]# vim expr.sh
#!/bin/bash
num1=$1
num2=$2
expr $1 + $2 &>/dev/null
[ $? -ne 0 ] && echo "请输入两个整数" && exit
echo "$num1+$num2=$[$num1+$num2]"
[root@test ~]# bash expr.sh 1 1.5
请输入两个整数
[root@test ~]# bash expr.sh 1 1
1+1=2
[root@test ~]# 

条件表达式

[ -f file ]
-e 文件存在则为真
-f 是否存在并且为普通文件
-d 是否为目录
-r 是否可读
-w 是否可写
-x 是否可执行

[root@test ~]# [ -e /etc ] && echo "存在"||echo "不存在"
存在
[root@test ~]# [ -e /etcc ] && echo "存在"||echo "不存在"
不存在
[root@test ~]# [ -f /etc ] && echo "存在"||echo "不存在"
不存在
[root@test ~]# [ -f /etc/hosts ] && echo "存在"||echo "不存在"
存在
[root@test ~]# 

数值表达式

[ 整数1 比较符 整数2 ]
-eq 等于
-ne 不等于
-gt 大于
-ge 大于等于
-lt 小于
-le 小于等于

[root@test ~]# [ 10 -eq 10 ] && echo "ok" ||echo "no"
ok
[root@test ~]# [ 10 -gt 10 ] && echo "ok" ||echo "no"
no
[root@test ~]# [ 10 -ge 10 ] && echo "ok" ||echo "no"
ok
[root@test ~]# [ 10 -lt 10 ] && echo "ok" ||echo "no"
no
[root@test ~]# [ 10 -le 10 ] && echo "ok" ||echo "no"
ok
[root@test ~]# [ 10 -ne 10 ] && echo "ok" ||echo "no"
no
[root@test ~]# 

多整数比较

[ 整数1 比较符 整数2 -o 整数3 比较符 整数4 ]
[ 整数1 比较符 整数2 -a 整数3 比较符 整数4 ]
-o  或
-a  与
[root@test ~]# [ 10 -eq 10 -o 10 -ne 10 ]
[root@test ~]# echo $?
0
[root@test ~]# [ 10 -eq 10 -a 10 -ne 10 ]
[root@test ~]# echo $?
1
[root@test ~]# 

字符串比较

#字符串比较必须加双引号
[root@test ~]# [ "$USER" = root ]
[root@test ~]# echo $?
0
[root@test ~]# [ "$USER" = "root" ]
[root@test ~]# echo $?
0
[root@test ~]# [ "$USER" = "opesn" ]
[root@test ~]# echo $?
1
[root@test ~]# [ ! "$USER" = "opesn" ]
[root@test ~]# echo $?
0
[root@test ~]# 

-z 值为0,则为真
-n 值不为0,则为真
[root@test ~]# [ -n "" ] && echo 0 || echo 1
1
[root@test ~]# [ -n "s" ] && echo 0 || echo 1
0
[root@test ~]# 
[root@test ~]# [ -z "s" ] && echo 0 || echo 1
1
[root@test ~]# [ -z "" ] && echo 0 || echo 1
0

正则对比

正则比对 [[]] 
取反 ! 写在表达式的前面
[root@test ~]# [[ $USER =~ ^r ]]
[root@test ~]# echo $?
0
[root@test ~]# [[ $USER =~ ^n ]]
[root@test ~]# echo $?
1
[root@test ~]# 
[root@test ~]# [[ ! $USER =~ ^n ]]
[root@test ~]# echo $?
0
[root@test ~]# [[ ! $USER =~ ^r ]]
[root@test ~]# echo $?
1
[root@test ~]# 

if判断

#单分支(单分支:一个条件一个结果)
[root@test test]# vim if.sh
#!/bin/bash
if [ -f /etc/passwd ];then
        echo "yes"
fi

#双分支(双分支:一个条件两个结果)
[root@test test]# vim if.sh
#!/bin/bash

if [ -f /etc/passwdd ];then
        echo "passwdd file is yes"
else
        echo "passwdd file is no"
fi

#多分支(多分支:多个条件多个结果)
[root@test test]# vim if.sh
#!/bin/bash

if [ -f /etc/passwdd ];then
        echo "passwdd file is yes"
elif [ -f /etc/passwddd ];then
        echo "passwddd file is yes"
elif [ -f /etc/passwd ];then
        echo "passwd file is yes"
fi

case语句

case 变量 in
    变量内容1)
            命令组
            ;;
    变量内容2)
            命令组
            ;;
    变量内容3)
            命令组
            ;;
    *)
            echo "请输入正确的变量"
esac

case语句书写

[root@test ~]# vim jumpserver.sh 
#!/bin/bash
clear
cat <<EOF
        * * * * * * * * * * * * * * *
        *       1.m01=10.0.1.61     *
        *       q.退出              *
        * * * * * * * * * * * * * * * 
EOF
trap "" INT HUP TSTP
while true
do
read -p "请输入你要连接的服务器:" sum
case $sum in
        1)
          clear
          ssh 10.0.1.61
          clear
          cat <<EOF
        * * * * * * * * * * * * * * *
        *       1.m01=10.0.1.61     *
        *       q.退出              *
        * * * * * * * * * * * * * * * 
EOF
          ;;
        q)
          exit
          ;;
        *)
                   cat <<EOF
        * * * * * * * * * * * * * * *
        *       1.m01=10.0.1.61     *
        *       q.退出              *
        * * * * * * * * * * * * * * * 
EOF
esac
done

for循环

for  变量名称 in  取值列表  (数字 字符串  `cat 文件` 命令)
do
        命令
        echo hehe
done

for循环书写

[root@test ~]# vim ping.sh 
#!/bin/bash
. /etc/init.d/functions
for i in 60..65
do
        
        ping -c1 -w1 10.0.1.$i &>/dev/null
        [ $? -eq 0 ] && action "ping 10.0.1.$i is ok" /bin/true
         &
done
wait
echo "在线取IP is ok"

while循环

while [ 条件 ] 为真则执行 为假退出

[root@test ~]# vim while_sum+.sh 
#!/bin/bash
sum=0
i=1
while true
do
        sum=$[$sum+$i]
        [ $i -eq 100 ] && echo $sum && exit
        ((i++))
done

语句控制

exit        #直接退出整个脚本
break       #跳出循环体 执行循环体外的命令
continue    #跳出当前的循环 继续下一次循环

exit

[root@test test]# cat exit.sh 
#!/bin/sh
while true
do
    echo hehe
    exit
done
echo done.......
[root@test test]# bash exit.sh 
hehe
[root@test test]# 

break

[root@test test]# cat break.sh 
#!/bin/sh
while true
do
    echo hehe
    break
done
echo done.......
[root@test test]# bash break.sh 
hehe
done.......
[root@test test]# 

continue

[root@test test]# cat continue.sh
#!/bin/sh
for i in 1..5
do
    [ $i -eq 4 ] && continue
    echo $i
done
echo done.......
[root@test test]# bash continue.sh
1
2
3
5
done.......
[root@test test]# 

函数

1) 完成特定功能的代码块
2) 代码模块化 便于复用和可读
3) 和变量类似 必须先定义在调用,如果不调用则函数不会执行

函数的定义方法

[root@test test]# cat function.sh
#!/bin/sh
fun1()
    echo "函数第一种定义方式"

function fun2 
    echo "函数第二种定义方式"

function fun3()
    echo "函数第三种定义方式"

fun1
fun2
fun3
[root@test test]# bash function.sh
函数第一种定义方式
函数第二种定义方式
函数第三种定义方式
[root@test test]# 

函数的传参

[root@test test]# cat fun.sh 
#!/bin/bash

fun() 
    echo 1
    echo "$1"
    return 100


fun $1
[root@test test]# bash fun.sh 2
1
2
[root@test test]# 

函数的本地变量local

[root@test test]# cat fun.sh
#!/bin/sh
fun1()
     local num=20
     for i in `seq 30`
     do
         total=$[num+i]
     done
     echo "结果为:$total"

fun1
echo $num
[root@test test]# bash fun.sh 
结果为:50

[root@test test]# 

return 返回值

[root@test test]# cat fun.sh
#!/bin/sh
fun()
    if [ -f "$1" ];then
       return 50
    else
       return 100
    fi

fun $1
re=$?
[ $re -eq 50 ] && echo "$1 文件存在"
[ $re -eq 100 ] && echo "$1 文件不存在"
[root@test test]# bash fun.sh /etc/hosts
/etc/hosts 文件存在
[root@test test]# bash fun.sh /etc/hostsssssssss
/etc/hostsssssssss 文件不存在
[root@test test]# 

数组

普通数组

只能使用数字作为索引
1) 数组的定义格式
第一种定义方式
数组名[索引名]  默认的索引从0开始
数组名[下标]=值
第二种定义方式
数组名=([0]=值 [1]=值 [20]=值)
第三种定义方式
array=(shell mysql kvm docker)
array=(shell mysql kvm docker [10]=test [20]=hehe)
2) 查看数组的值
echo $array[0]
 3) 查看数组的索引
[root@backup ~]# echo $!array[*]
[root@test test]# cat array.sh
#!/bin/bash
IPS=(
114.114.114.114
223.5.5.5
)
for i in $IPS[*]
do
    ping -c 1 -W 1 $i &>/dev/null
    [ $? -eq 0 ] && echo ok|| echo error
done
[root@test test]# bash array.sh
ok
ok
[root@test test]# 

关联数组

declare -A array  声明关联数组
[root@backup ~]# declare -A array
[root@backup ~]# array[index1]=shell
[root@backup ~]# array[index2]=MySQL
[root@backup ~]# array[index3]=KVM
[root@backup ~]# echo $array[*]
shell MySQL KVM
[root@backup ~]# echo $!array[*]
index1 index2 index3
[root@test test]# cat sex.txt 
m
m
m
f
f
f
m
f
m
x
[root@test test]# cat array.sh
#!/bin/sh
declare -A array
while read line
do
    let array[$line]++
done<sex.txt
for i in $!array[*]
do
    echo "$i 出现了 $array[$i] 次"

done
[root@test test]# bash array.sh
f 出现了 4 次
m 出现了 5 次
x 出现了 1 次
[root@test test]# 

以上是关于shell编程的主要内容,如果未能解决你的问题,请参考以下文章

Shell脚本编程

linux12shell编程 --> shell基础01

Shell编程Shell中多分支case条件语句

Shell编程Shell中for循环while循环until循环语句

Shell编程Shell中for循环while循环until循环语句

1shell编程(shell脚本)_理解编程和变量