Linux_Shell脚本的艰辛之路(初级篇)

一,shell脚本基础

1,shell脚本的简单小案例

写一个shell脚本,实现ping一个网站,如果ping的通,打印成功信息,否则打印失败信息

创建firstshell.sh文件,写入以下内容,保存并运行sh firstshell.sh

ping -c1 wwwwww.baidu.com &>/dev/null  && echo "www.baidu.com is ok" || echo "www.baidu.com is failed"
  • &>/dev/null 这个表示把前面的操作的输出写到一个文件里,null即隐藏掉,不需要看到输出结果
  • && ......|| ....... 这个表示的和if/else差不多,前面条件如果成立执行&&后面的内容,否则执行||后面的内容

看下面的命令,分号是分隔同一行的多个命令,回车会执行所有的命令,但是前面的命令如果执行失败是不会影响后面的执行的,即使执行失败也会执行。

cd /usr/test;ls

2,echo输出颜色文本

输出文本的颜色:30m-37m为文字颜色,40m-47m为文字背景颜色

echo -e "\e[1;32m打印的内容\e[0m"

3,脚本中使用变量

shell脚本变量?用一个固定的字符串去表示不固定的内容

例如上面的脚本

ping -c1 wwwwww.baidu.com &>/dev/null  && echo "www.baidu.com is ok" || echo "www.baidu.com is failed" 

如果www.baidu.com这个地址修改了,那么至少需要修改三处,那么这里可以使用变量效果是一样的

#! /usr/bin/bash
ip=www.baidu.com
ping -c1 $ip &>/dev/null  && echo "$ip is ok" || echo "$ip is failed"  

再改进一下也可以这样写

#!/usr/bin/bash
ip=www.baidu.com
if ping -c1 $ip &>/dev/null;then  
         echo "$ip is ok" 
else 
         echo "$ip is failed" 
fi

也可以这样写

$?是上面的命令执行的结果,[ $? -eq 0 ]是对结果进行判断,执行是否成功,左右都要有空格!

#!/usr/bin/bash
ip=www.baidu.com
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ];then
         echo "$ip is ok"
else
         echo "$ip is failed"
fi

还可以手动动态输入

#!/usr/bin/bash
read -p "请输入IP值:" ip
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ];then
         echo "$ip is ok"
else
         echo "$ip is failed"
fi

请输入IP值:www.baidu.com
www.baidu.com is ok

4,环境变量及自定义变量

自定义变量:

  • 定义变量:变量名=变量值,变量名必须以字母或下划线开头,区分大小写 ip=1
  • 引用变量:$变量名 或 $
  • 查看变量:echo $变量名 set(所有变量:包括自定义变量和环境变量)
  • 取消变量:unset 变量名
  • 作用范围:仅在当前shell中有效

环境变量:

  • 定义环境变量:

    • 方法一:export dir=/home/test
    • 方法二:export dir 将自定义变量转换成环境变量
  • 引用环境变量:$变量名或$

  • 查看环境变量:echo $变量名 env 例如 env | grep dir

  • 取消环境变量:unset变量名

  • 变量作用范围:在当前shell和子shell中有效

5,位置变量和预定义变量

https://blog.csdn.net/Y24678750382/article/details/89362282

定义或引用变量时注意事项:

双引号是弱引用,单引号是强引用,单引号里的变量不会显示成变量值,会原封不动的显示变量名。

``命令替换 等价于$() 反引号中的shell命令会被先执行

touch `date+%F`_file.txt
这两个效果是一样的
touch $(date+%F)_file.txt

这样就创建了一个名为2021-05-07_file.txt文件

6,变量的运算 整数小数

整数运算:

  • 方法一:expr

    • expr 1+2
    • expr $num1 + $num2
  • 方法二:$(())

    • echo $(($num1+$num2))
    • echo $((5-3*2))
  • 方法三:$[]

    • echo $[5+2]
  • 方法四:let(最常用)

    • let sum=2+3;echo $sum
    • let i++;echo $i

这里带出for循环和while循环

#!/usr/bin/bash
ip=www.baidu.com
for i in {1..5}
do
        ping -c1 $ip &>/dev/null
        if [ $? -eq 0 ];then
                echo "$ip is up..."
        fi
done
#!/usr/bin/bash
ip=www.baidu.com
i=1
while [ $i -le 5 ]
do
        ping -c1 $ip &>/dev/null
        if [ $? -eq 0 ];then
                echo "$ip is up..."
        fi
        let i++
done

7,变量内容的删除和替换

其实就是截取字符串之类的操作

字符串变量截取、替换和删除:https://blog.csdn.net/qq_42491125/article/details/101368505

8,shell条件测试概述

包括文件比较、数字比较、字符串比较

格式1:test 条件表达式

格式2:[ 条件表达式 ]

格式3:[[ 条件表达式 ]]

=== 文件测试 [ 操作符 文件或目录 ]

  • test -d /home 检测/home目录是否存在
  • [ -d /home ] 检测/home目录是否存在
#!/usr/bin/bash
back_dir=/usr/local/mysql_back
#if ! test -d $back_dir;then   如果目录不存在,则创建目录
if [ -d $back_dir ];then
        mkdir -p $back_dir
fi
echo "开始备份..."
if [ $USER != "root" ];then
        echo "你没有权限!"
        exit
fi
echo "开始备份..."
  • [ -f file ] 是否存在,而且是文件
  • [ -r file ] 当前用户对该文件是否有读权限
  • [ -x file ]
  • [ -w file ]
  • [ -L file ] 判断是否是链接

=== 数值比较 [ 整数1 操作符 整数2 ]

  • [ 1 -gt 10 ] 大于
  • [ 1 -lt 10 ] 小于
  • [ 1 -eq 10 ] 等于
  • [ 1 -ne 10 ] 不等于
  • [ 1 -ge 10 ] 大于等于
  • [ 1 -le 10 ] 小于等于

这里写一个小例子,写一个脚本创建用户,如果用户存在,提示,如果不存在,创建用户

id 用户名:这个命令可以查看用户信息

#!/usr/bin/bash
read -p "Please input a username: " user
id $user &>/dev/null
if [ $? -eq 0 ];then
        echo "user $user already exists"
else
        useradd $user
        if [ $? -eq 0 ];then
                echo "$user is created successful!"
        fi
fi

grep结合awk简单用法

=== 字符串比较

提示:使用双引号

[ "$USER" = "root" ];echo $?
[ "$USER" == "root" ];echo $?
[ -z "$USER" ]  字符长度是否为0
[ -n "$USER" ]  字符长度是否不为0

变量为空或未定义,长度都为0

9,内存磁盘使用告警

再写一个小例子,如果根分区的磁盘容量如果超过90%,就报警

查看磁盘容量

[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# df -Th
Filesystem Type Size Used Avail Use% Mounted on
devtmpfs devtmpfs 869M 0 869M 0% /dev
tmpfs tmpfs 879M 0 879M 0% /dev/shm
tmpfs tmpfs 879M 664K 878M 1% /run
tmpfs tmpfs 879M 0 879M 0% /sys/fs/cgroup
/dev/vda1 ext4 40G 25G 13G 67% /
tmpfs tmpfs 176M 0 176M 0% /run/user/996
tmpfs tmpfs 176M 0 176M 0% /run/user/0

定位到跟目录

[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# df -Th | grep '/$'
/dev/vda1 ext4 40G 25G 13G 67% /

打印倒数第二列(也就是使用百分比)

[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# df -Th | grep '/$' | awk '{print $(NF-1)}'
67%

[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# df -Th | grep '/$' | awk '{print $(NF-1)}' | awk -F"%" '{print $1}'
67

#!/usr/bin/bash
disk_use=`df -Th | grep '/$' | awk '{print $(NF-1)}' | awk -F"%" '{print $1}'`
if [ $disk_use -ge 60 ];then
        echo "`date +%F-%H` disk: ${disk_use}% disk war..."
fi

10,case多条件匹配

#!/usr/bin/bash
case "$1" in
test)
        echo "this is test"
        ;;
test11)
        echo "this is test11"
        ;;
*)
        echo "others"
        ;;
esac

[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# sh firstshell.sh test
this is test
[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# sh firstshell.sh test11
this is test11
[root@iZ2ze9ipfjhle3cz3cgb0rZ scripts]# sh firstshell.sh test11sdf
others

11,case删除用户判断

首先使用if判断

#!/usr/bin/bash
################################
#delete user                   #
#v1.0 by ftx 2021/5/7          #
################################

read -p "Please input a username: " user
id $user &>/dev/null
if [ $? -ne 0 ];then
        echo "no such user: $user"
        exit
fi

read -p "Are you sure?[y/n]: " action
#  -o 是或者的意思
if [ "$action"="y" -o "$action"="Y" ];then
        userdel -r $user
        echo "$user is deleted!"
fi

其次再用case判断

#!/usr/bin/bash
################################
#delete user                   #
#v1.0 by ftx 2021/5/7          #
################################

read -p "Please input a username: " user
id $user &>/dev/null
if [ $? -ne 0 ];then
        echo "no such user: $user"
        exit
fi

read -p "Are you sure?[y/n]: " action
case "$action" in
y|Y|yes|YES)
        userdel -r $user
        echo "$user is deleted!"
        ;;
*)
        echo "error"
esac

二,shell脚本实践

1,case实现jumpServer堡垒机

什么是堡垒机?堡垒机就是为了避免直接连接到主机所造成的安全性问题,每次连接主机时,需要先登录堡垒机,然后通过堡垒机再与多个主机进行连接。

在生产环境中

业务服务器不允许直接连接,允许通过从跳板机连接

业务服务器不允许root用户直接登录

实现堡垒机的思路

  • 登录一个用户,这个用户提前创建好,例如alice,这个用户一登录就触发shell脚本执行
  • 脚本内容是根据选择不同的选项连接不同的主机
useradd alice
passwd alice

创建了用户之后,写一个脚本

#! /usr/bin/bash
#jumpserver

# 捕捉ctrl+c 强制退出信号,什么也不做
trap "" HUP INT OUIT TSTP

clear  # 清除屏幕
while :
do
        cat <<-EOF
        +-------------------------------------+
        |    1,first host:101.201.101.206     |
        |    2,second host:101.208.108.228    |
        |    3,third host:192.168.101.298     |
        +-------------------------------------+
        EOF

        # 这里为了模拟三台机器,就使用一台即可
        firsthost=101.201.101.206
        secondhost=101.201.101.206
        thirdhost=101.201.101.206

        read -p "Please select one number: " num
        case "$num" in
        1)
                ssh root@$firsthost
                ;;
        2)
                ssh root@$secondhost
                ;;
        3)
                ssh root@$thirdhost
                ;;
        "")
                ;;
        *)
                echo "error"
        esac
done

那么如何让用户一登录就首先自动执行脚本呢?

把上面写的那个jumpserver.sh脚本放到/home/alice/下(就是哪个用户进行登录的,哪个脚本放到他的下面,就会自动执行)

然后编辑 .bash_profile文件,把脚本路径配置里面

vi .bash_profile

一定要切换到alice用户并进入到/home/alice的目录中,进行vim

并且登录root用户要授予alice用户对jumpserver.sh脚本的权限

# 查看alice用户属于哪个用户组
groups alice
# 授权限
chown alice:alice /home/alice/jumpserver.sh
chmod 777 /home/alice/jumpserver.sh
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/.local/bin:$HOME/bin

export PATH
/home/alice/jumpserver.sh #配上这个

测试:断开连接,使用alice用户重新连接,脚本就会登录后立即执行

执行效果如下

+-------------------------------------+
|    1,first host:101.201.101.206     |
|    2,second host:101.208.108.228    |
|    3,third host:192.168.101.298     |
+-------------------------------------+
Please select one number: 1
root@101.201.101.206's password: 
Last login: Sun May 16 13:37:00 2021

Welcome to Alibaba Cloud Elastic Compute Service !

[root@iZ2ze9ipfjhle3cz3cgb0rZ ~]# cd /
[root@iZ2ze9ipfjhle3cz3cgb0rZ /]# ls
bin  boot  dev  disk  etc  home  lib  lib64  lost+found  media  ml  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
[root@iZ2ze9ipfjhle3cz3cgb0rZ /]# exit
logout
Connection to 101.201.101.206 closed.
+-------------------------------------+
|    1,first host:101.201.101.206     |
|    2,second host:101.208.108.228    |
|    3,third host:192.168.101.298     |
+-------------------------------------+
Please select one number: 

2,for实现批量主机ping探测

下面是简单的一个实现,写死的一个2到20的序列

#!/usr/bin/bash
# ping check
# v1.0 by ftx 2021-5-16

>ip.txt
for i in {2..20}
do 
        # {}&的作用是放后台执行
        {
        ip=192.168.122.$i
        ping -c1 -W1 $ip &>/dev/null
        if [ $? -eq 0 ];then
                echo "$ip" |tee -a ip.txt # 把打印的信息放在ip.txt文件中
        }&
done
wait # wait的作用是等待后台执行完毕
echo "finished..."

如何有指定好的几个ip怎么办呢?可以先把维护到一个txt中,然后对txt进行循环

ip.txt

192.168.1.101
192.168.1.102
192.168.1.103
#!/usr/bin/bash
>ip.txt
for i in `cat ip.txt`
do 
        # -W1 等待超时时间
        ping -c1 -W1 $ip &>/dev/null
        if [ $? -eq 0 ];then
                echo "$ip is up."
        else
                echo "$ip is down."
        fi
done

3,for实现批量用户创建

#!/usr/bin/bash
clear
while :
do
        read -p "Please enter prefix & pass & num[tianyun 123 5]: " prefix pass num
        printf "user infomation:
        -----------------------
        user prefix: prefix
        user pass: pass
        user num: 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
                echo "$pass" | passwd --stdin $user &>/dev/null
                if [ $? -eq 0 ];then
                        echo "$user is created.."
                fi
        fi

done

实现效果

Please enter prefix & pass & num[tianyun 123 5]: ftx 23 6
user infomation:
	-----------------------
	user prefix: prefix
	user pass: pass
	user num: num
	----------------------
	Are you sure?[y/n]: n
Please enter prefix & pass & num[tianyun 123 5]: 

4,for实现批量远程主机ssh配置

使用场景:有多台主机,需要同时修改ssh的一些配置,如果一台一台的修改就很麻烦,可以写一个脚本批量处理

首先用一个文件ip.txt存储主机ip,用于for循环

192.168.1.101
192.168.1.102
192.168.1.103

然后写脚本

#!/usr/bin/bash
for ip in `cat ip.txt`
do
        {
        ping -c1 -W1 $ip &>/dev/null
        if [ $? -eq 0 ];then
                # sed -ri 用于修改文件内容 /^这个是找以#UseDNS开头的行,/c为替换成后面的内容 这里的效果也就是把#UseDNS no改成UseDNS no
                ssh $ip "sed -ri '/^#UseDNS/cUseDNS no' /etc/ssh/sshd_config"
        fi
        }&

done
wait
echo "all ok..."

5,while unitl 测试远程主机连接

当可以ping通时,一直while循环,直到ping不通,然后打印。

#!/usr/bin/bash
ip=101.101.202.303
while ping -c1 -W1 $ip &>/dev/null
do
        sleep 1  # 当ping通时,一直while循环,每隔1秒执行
done 
echo "$ip is down.."

until是相反的,如果ping不通,一直循环,直到ping通,然后打印。

#!/usr/bin/bash

ip=101.101.202.303
until ping -c1 -W1 $ip &>/dev/null
do
        sleep 1
done 
echo "$ip is up.."

6,Expect实现ssh非交互登录

什么是Expect?https://www.cnblogs.com/snailon/p/12529761.html

首先需要安装Expect

yum install -y expect
#! /usr/bin/expect
spawn ssh root@localhost

expect{
	# 如果收到含有yes/no的消息,就自动输入yes并回车,如果没有收到消息,就继续
	"yes/no" { send "yes\r"; exp_continue }
	"password" { send "AIni12345\r" }
}
interact # 保持登录进入状态

然后使用expect执行脚本

expect expect.sh

由于expect安装碰见点儿问题,下次再继续!

三,shell脚本基础的深入

1,Array数组的基本概念

普通数组

赋值方式

books=(linux shell awk openstack)

books[0]=pear

books[1]=apple

根据索引取值

echo ${books[0]}

关联数组

在使用关联数组之前一定要先声明这个关联数组!declare -A info

赋值方式

info=([name]tianyu [sex]male [age]36)

info=(`cat /etc/password`) 希望是将该文件中的每个行为作为一个元数据赋值给数组

info=(tom jack "bash shell")

根据名称取值

echo ${info[name]}

2,Array数组的赋值和遍历

#!/usr/bin/bash
# 读取/etc/hosts文件的每一行,然后把每一行放到数组里面
while read line
do
        hosts[++i]=$line
done </etc/hosts

echo "第一行的内容:${hosts[1]}"
echo # 空行
# ${!hosts[@]} 获取数组的每个索引
for i in ${!hosts[@]}
do
        echo "$i: ${hosts[i]}"
done

3,Array实现性别统计

#!/usr/bin/bash
# 读取/etc/hosts文件的每一行,然后把每一行放到数组里面
declare -A sex # 声明关联数组
while read line
do
        type=`echo $line |awk '{print $2}'`  #命令的意思:拿到$line这一行,然后得到打印的第二列
        let sex[$type]++
done </etc/hosts

# ${!hosts[@]} 获取数组的每个索引
for i in ${!hosts[@]}
do
        echo "$i: ${sex[i]}"
done

4,Function函数的定义和调用

完成特定功能的代码块,便于复用代码,函数必须先定义才可以使用

#! /bin/bash
test() {
value=1
for((i=1;i<=5;i++))
do
        value=$[$value * $i]
done
echo "5的阶乘是:$value"
}

#调用方法不用小括号
test

5,Function函数的返回值Return Out

第一种得到返回值的方式

#! /bin/bash
test() {
read -p "enter num: " num
return $[2*$num] # 返回输入数字乘以2的结果
}

#调用方法不用小括号
test
echo "test方法的返回值是:$?"

第二种得到返回值的方式(推荐)

把方法的执行结果直接赋值给一个变量即可

#! /bin/bash
test() {
read -p "enter num: " num
return $[2*$num] # 返回输入数字乘以2的结果
}

#调用方法不用小括号
result=`test`
echo "test方法的返回值是:$result"

6,函数传参

位置参数

#! /bin/bash
if [ $# -ne 3 ];then
        echo "usage: `basename $0` par1 par2 par3"
        exit
fi

func3() {
        echo "$(($1 * $2 * $3))"
}

result=`func3 $1 $2 $3`
echo "result is: $result"

数组变量

给函数传数组参数

num=(1 2 3 4 5)
array() {
	local value=1  # local 局部变量,只在本方法中有效
	for i in "$@"   # $@是获取所有的参数
    do
    	value=$[value * $i]
    done
    echo "$value"
}
array ${num[@]}

7,Sed流编辑器

sed 是一种在线编辑器,它一次处理一行内容。
处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有 改变,除非你使用重定向存储输出。
sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。

sed命令的格式

  • sed 选项 '动作' 文件名
  • cat 文件名 | sed 选项 '动作'

选项:
-n :只显示被修改的行的内容
-e :直接在命令列模式上进行 sed 的动作编辑;
-f :直接将 sed 的动作写在一个文件内, -f filename 则可以运行 filename 内的 sed 动作;
-r :sed 的动作支持的是延伸型正规表示法的语法。(默认是基础正规表示法语法)
-i :直接修改读取的文件内容,而不是输出到终端。

动作:
a :在指定行后 新增一行或多行内容
c :替换指定行的内容
d :删除指定行的内容
i :在指定行之前 插入一行或多行内容
s :替换指定内容

具体操作见此文章:https://www.cnblogs.com/lfjn/p/11455970.html

四,linux定时任务

写一个linux定时任务,定时执行shell脚本。

  • crond是Linux系统用来定期执行命令或指定程序的服务的一种服务或软件。

  • 一般情况下安装完成Linux操作系统之后,默认便会启动Crond任务调用服务。

  • Crond服务会定期(默认一分钟检查一次)检查系统中是否有要执行的任务工作。如果有,便根据其预先设定的定时任务规则自动执行该定时任务工作。

  • crontab -e 进入当前用户的定时任务vim编辑模式

  • crontab -l 查看当前用户的定时任务配置

  • crontab -u 用户名 -l 查看指定用户的定时任务设置

注:crontab { -l | -e } 实际上就是在操作/var/spool/cron/当前用户这样的文件,root用户就是/var/spool/cron/root

直接编辑/etc/crontab 文件,即vi /etc/crontab,添加相应的定时任务

1,定时任务表达式规则

5 * * * *每小时第5分钟执行
*/5 * * * *每5分钟执行
0 2 * * * 每天凌晨2点执行

2,直接编辑/etc/crontab 文件

minute - 从0到59的整数
hour - 从0到23的整数
day - 从1到31的整数 (必须是指定月份的有效日期)
month - 从1到12的整数 (或如Jan或Feb简写的月份)
dayofweek - 从0到7的整数,0或7用来描述周日 (或用Sun或Mon简写来表示)
root - 表示以root用户身份来运行
command - 需要执行的命令(可用as ls /proc >> /tmp/proc或 执行自定义脚本的命令)
run-parts表示后面跟着的是一个文件夹,要执行的是该文件夹下的所有脚本
对于以上各语句,星号()表示所有可用的值。例如在指代month时表示每月执行(需要符合其他限制条件)该命令。
整数间的连字号(-)表示整数列,例如1-4意思是整数1,2,3,4
指定数值由逗号分开。如:3,4,6,8表示这四个指定整数。
符号“/”指定步进设置。“/”表示步进值。如0-59/2定义每两分钟执行一次。

3,记录定时任务日志

# 不记录日志,在每一行定时任务后面加上:

> /dev/null 2>&1

# 日志记录追加到指定文件

>> /path/mylog.log 2>&1

说明:
> /dev/null 将输出重定向到/dev/null,这是个空设备,也就是忽略其输出。
> 2>&1 是将错误输出到标准输出,如果在控制台调试,也就是屏幕上,方便调试。
有3个默认的i/o,0是标准输入,一般是键盘;1是标准输出,一般是屏幕了;2是标准错误。

4,可能遇到的问题

当crontab -e编辑定时任务时,报 linux no crontab for root - using an empty one Permission denied 错误

https://blog.csdn.net/weixin_42374607/article/details/107437764

报 bash: /usr/bin/chattr: Permission denied 错误

https://blog.csdn.net/handsomezls/article/details/116130125

http://www.voidcn.com/article/p-xecghhrc-bua.html

五,Linux脚本--实现自动根据名称查询进程号并杀死进程

今天,写脚本的时候,有这么一个需求,先查询进程号,然后再kill掉进程,不过该过程要通过脚本实现。
一般来说,直接用Linux命令,可以这样

先使用 ps -ef|grep Linux-new.py 查询出进程号,然后,在用kill -9 进程号 , kill掉进程,但是,这是手工输得,程序不能手工输入呀,故写一个脚本实现

Linux_new_id=`ps -ef | grep Linux-new.py | grep -v "grep" | awk '{print $2}'`
echo $Linux_new_id
 
for id in $Linux_new_id
do
    kill -9 $id  
    echo "killed $id"  
done
posted @ 2021-05-21 13:51  你樊不樊  阅读(246)  评论(0编辑  收藏  举报