Linux学习08_学习shell脚本编程

为什么要学习shell脚本

用shell脚本可以做以下这些事情:

  • 自动化管理:很多任务和需求可以实现自动化
  • 跟踪与管理系统:事实上很多系统服务都是通过shell脚本启动的
  • 简单入侵检测:定时扫描系统日志,就可以检测入侵行为
  • 数据处理
  • 跨平台的处理

第一个脚本的编写

shell脚本其实就是纯文本文件,有着如下这些规则

  1. 命令从上而下,从左到右执行
  2. 命令、参数、选项之间的多个空格会被忽略
  3. 空白行和tab会被忽略
  4. 如果读取到Enter,就会开始执行命令
  5. 多行命令可以用\来进行连接
  6. #可以用作注释

接下来我们来编写一个hello world:

python 复制代码
[root@node4 bin]# vim hello.sh
python 复制代码
#!/bin/bash
# show hello world to you
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Hello World! \a \n"
exit 0

接下来简单说明下每句话的含义

  1. 第一行声明了这个脚本使用的shell名称
  2. 其余的#内容是注释,一般会说明这个脚本的作用,脚本作者等
  3. 主要环境变量的声明,一般比较重要的是PATH和LANG
  4. 主程序部分,本脚本来说就是echo那一行
  5. 执行结果,如果正常执行返回0

执行脚本,可以通过以下命令执行

python 复制代码
sh hello.sh

如果想通过./来执行,需要先添加权限

python 复制代码
[root@node4 bin]# ./hello.sh
-bash: ./hello.sh: 权限不够
[root@node4 bin]# chmod a+x hello.sh
[root@node4 bin]# ./hello.sh
Hello World!  

简单shell脚本练习

通过一些练习逐渐掌握shell脚本编写

1、交互式脚本

python 复制代码
[root@node4 bin]# vim showname.sh
#!/bin/bash
# 用户输入姓名,程序打印完整姓名
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "请输入你的姓:" lastname # 提示输入
read -p "请输入你的名:" firstname
echo -e "\n你的完整名字是: ${lastname} ${firstname}" # 打印输出

运行结果

python 复制代码
[root@node4 bin]# sh showname.sh 
请输入你的姓:koizumi
请输入你的名:hanayo

你的完整名字是: koizumi hanayo

2、利用date建立文件

python 复制代码
[root@node4 bin]# vim create_file_by_date.sh
#!/bin/bash
# 根据日期生成文件
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "我将使用touch命令来生成3个文件" # 打印提示信息
read -p "请输入文件名称" fileuser # 提示用户输入
filename=${fileuser:-"filename"} # 如果无输入,使用默认值
date1=$(date --date='2 days ago' +%Y%m%d)
date2=$(date +%Y%m%d)
file1=${filename}_${date1}
file2=${filename}_${date2}
touch "${file1}"
touch "${file2}"

运行结果如下:

python 复制代码
[root@node4 bin]# sh create_file_by_date.sh 
我将使用touch命令来生成3个文件
请输入文件名称myfile
[root@node4 bin]# ls
create_file_by_date.sh  hello.sh  myfile_20240416  myfile_20240418  showname.sh

3、简单数值运算

bash shell里仅支持整数的运算

python 复制代码
#!/bin/bash
# 简单数学运算
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "请输入2个整数"
read -p "第一个整数:" numa
read -p "第二个整数:" numb
total=$((${numa}+${numb}))
echo -e "\n${numa}+${numb}的计算结果是==>${total}" 

运行结果

python 复制代码
[root@node4 bin]# sh math_operation.sh 
请输入2个整数
第一个整数:1
第二个整数:1

1+1的计算结果是==>2

脚本执行类别的差异

脚本可以用source、sh和./来执行,那么这三种执行方式的差异是什么呢?

  • 当使用sh的方式执行时,系统会开启一个bash的子进程来执行程序
  • 利用source来执行是,则在父进程中执行

以上面的showname脚本来举例:

python 复制代码
[root@node4 bin]# echo ${firstname} ${lastname}
# 可以看到执行前,这两个变量不存在
[root@node4 bin]# sh showname.sh 
请输入你的姓:hanayo
请输入你的名:kayotin

你的完整名字是: hanayo kayotin
[root@node4 bin]# echo ${firstname} ${lastname}
#在执行后,这两个变量还是不存在

用source执行:

python 复制代码
[root@node4 bin]# source showname.sh 
请输入你的姓:watanabe
请输入你的名:yo

你的完整名字是: watanabe yo
[root@node4 bin]# echo ${firstname} ${lastname}
yo watanabe
# 可以看到变量是存在的

判断式的使用

之间我们简单介绍过使用&&或者||基于前一个命令的执行结果来执行下一条命令,那时候我们是用的$?这个变量来判断的。除此之外还有其他其中判断方法

使用test命令

检测某个目录是否存在

python 复制代码
[root@node4 ~]# test -e /kayotin
[root@node4 ~]# test -e /kayotin && echo "目录存在" || echo "目录不存在"
目录不存在
[root@node4 ~]# test -e /root && echo "目录存在" || echo "目录不存在"
目录存在

其他一些常用参数:

  1. 文件类型的判断:

    python 复制代码
    -e 路径是否存在
    -f 路径是否是文件
    -d 路径是否是目录
  2. 文件权限检测

    python 复制代码
    -r 是否具有读权限
    -w 写权限
    -x 执行权限
  3. 两个文件之间的比较

    python 复制代码
    -nt 文件1是否比文件2新
    -ot older than
    -ef 是否为同一个文件,判断hardlink
  4. 两个整数之间的比较

    python 复制代码
    -eq 相等
    -ne 不等
    -gt 大于
    -lt 小于
    -ge 大于等于
    -le 小于等于
  5. 字符串判断

    python 复制代码
    test -z string 判断字符串是否为空,如果是空字符串,则为true
    test -n string 判断是否为非0
    test str1 == str2 判断是否相等
    test str1 != str2 判断是否不相等
  6. 多重条件判定

    python 复制代码
    -a 两个条件是否同时成立
    -o or

使用test的例子

我们使用test写一个脚本,实现如下功能:

  1. 让用户输入一个路径,判断路径是否存在,不存在则输出不存在,然后退出
  2. 如果存在,判断是文件还是目录,输出判断结果
  3. 判断文件权限,然后输出结果
python 复制代码
[root@node4 bin]# vim check_file.sh
#!/bin/bash
# 用test命令判断文件
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "请输入完整路径:" file_path
# 判断字符串是否为空
test -z ${file_path} && echo "路径不能为空!" && exit 0
# 判断路径是否存在
test ! -e ${file_path} && echo "路径不存在" && exit 0
# 判断是目录还是文件
test -f ${file_path} && file_type="一般文件"
test -d ${file_path} && file_type="目录"
test -r ${file_path} && file_perm="读"
test -w ${file_path} && file_perm="${file_perm}写"
test -x ${file_path} && file_perm="${file_perm}执行"
# 输出
echo "路径:${file_path}是一个${file_type}"
echo "权限是${file_perm}"

执行效果如下:

python 复制代码
[root@node4 bin]# sh check_file.sh 
请输入完整文件路径:/root/myfile
路径:/root/myfile是一个一般文件
权限是读写

使用[]命令进行判断

使用中括号命令进行判断也是一种常用方式,

python 复制代码
[root@node4 bin]# vim y_n.sh
#!/bin/bash
# 判断用户输入
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "请输入y或者n:" yn
[ "${yn}" == "Y" -o "${yn}" == "y" ] && echo "你输入了y" && exit 0
[ "${yn}" == "N" -o "${yn}" == "n" ] && echo "你输入了n" && exit 0
echo "非法输入" && exit 0

注意,使用中括号的判断,前后一定要有空格,也就是[空格 命令。。。空格]

shell脚本中的默认变量$0...

系统命令都有很多的参数,例如ls -la,shell脚本也是如此,

python 复制代码
/文件/具体/路径.sh opt1 opt2 opt3 opt 4
$0                 $1   $2   $3   $4

我们用一个例子来理解一下:

python 复制代码
[root@node4 bin]# vim paras.sh
#!/bin/bash
# 展示默认参数的使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "本shell脚本的名字是 ==> ${0}"
echo "参数的总个数是 ==> $#"
[ "$#" -lt 2 ] && echo "参数总数小于两个" && exit 0
echo "全部参数是 ==> '$@'"
echo "第一个参数是 == > ${1}"
echo "第二个参数是 == > ${2}"

运行结果如下:

python 复制代码
[root@node4 bin]# sh paras.sh a b c
本shell脚本的名字是 ==> paras.sh
参数的总个数是 ==> 3
全部参数是 ==> 'a b c'
第一个参数是 == > a
第二个参数是 == > b

用shift命令可以对参数进行偏移

条件判断语句

所有编程语言里面都有条件判断,就是if之类的,当然shell也是如此

if...then

if...then是shell中最常见的判断条件,基本语法如下

python 复制代码
if [ 条件判断语句 ] ; then
     条件成立时,需要执行的语句
fi <==表示结束if

我们将之前那个输入y或n的脚步改写成if...then的样子

python 复制代码
[root@node4 bin]# vim y_n_if.sh
#!/bin/bash
# if then的使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "请输入y或者n:" yn
if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]; then
        echo "你输入了y"
        exit 0
fi
if [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; then
        echo "你输入了n"
        exit 0
fi
echo "非法输入!"

运行如下

python 复制代码
[root@node4 bin]# sh y_n_if.sh 
请输入y或者n:y
你输入了y
[root@node4 bin]# sh y_n_if.sh 
请输入y或者n:a
非法输入!

if...then...else

shell语言也有else语句和else if语句来应对复杂的条件判断,基本语法如下

python 复制代码
if [ 判断条件1 ]; then
    条件1成立时,执行的语句
elif [ 判断条件2 ]; then
    条件2成立时,执行的语句
else 
		以上条件都不成立时执行
fi

我们使用if...else改写一下上一个程序

python 复制代码
[root@node4 bin]# vim y_n_else.sh
#!/bin/bash
# if else的使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "请输入y或者n:" yn
if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]; then
        echo "你输入了y"
        exit 0
elif [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; then
        echo "你输入了n"
        exit 0
else
        echo "非法输入!"
fi

用if...else结合之前的参数功能

python 复制代码
[root@node4 bin]# vim hello_para.sh
#!/bin/bash
# if else结合参数的使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
if [ "${1}" == "hello" ]; then
        echo "how are you--"
elif [ "${1}" == "" ]; then
        echo "请输入参数,例如 ==> {${0} para}"
else
        echo "仅支持hello参数,例如 ==> {${0} hello}"
fi

再举一个例子,我们用netstat命令配合if...esle来检测端口开放情况,如果无法使用netstat命令,可以用yum install net-tools来安装:

python 复制代码
[root@node4 bin]# vim netstat.sh
#!/bin/bash
# if else配合检测端口
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "即将进行端口检测..."
testfile=/root/bin/net_check.txt
netstat -tuln > ${testfile}
testing=$(grep ":80 " ${testfile})
if [ "${testing}" != "" ]; then
        echo "80端口是开放的。"
fi
testing=$(grep ":22 " ${testfile})
if [ "${testing}" != "" ]; then
        echo "22端口是开放的。"
fi
testing=$(grep ":25 " ${testfile})
if [ "${testing}" != "" ]; then
        echo "25端口是开放的。"
fi

case...esac语句

case语句在python里没有,在其他语言也挺常见的,shell中的case语句基本语法如下:

python 复制代码
case  $变量名称 in
		"变量1")
		执行的内容
		;;
		"变量2")
		执行的内容
		;;
esac   <== 表示结束case语句

用下面这个例子来直观感受一下

python 复制代码
[root@node4 bin]# vim hello_case.sh
#!/bin/bash
# case语句使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
case ${1} in
 "hello")
        echo "你好"
        ;;
 "")
        echo "请输入至少一个参数"
        ;;
 *)
        echo "仅能使用如下参数 ${0} {hello}"
        ;;
esac

函数function

shell中的函数基本语法如下:

python 复制代码
function funcname() {
	......
}

简单举例如下:

python 复制代码
[root@node4 bin]# vim func123.sh
#!/bin/bash
# function函数的使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
function printit() {
        echo -n "你的选择是:"
}
echo "本程序将会打印你的输入"
case ${1} in
 "one")
        printit; echo ${1} | tr 'a-z' 'A-Z'
        ;;
 "two")
        printit; echo ${1} | tr 'a-z' 'A-Z'
        ;;
 *)
        echo "仅支持如下参数: ${0} {one|two}"
        ;;
esac

我们可以往函数中传递参数,将上面例子简单改写一下

python 复制代码
[root@node4 bin]# vim func123-1.sh
#!/bin/bash
# functi:on函数的使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
function printit() {
        echo -n "你的选择是:${1} "
}
echo "本程序将会打印你的输入"
case ${1} in
 "one")
        printit 1
        ;;
 "two")
        printit 2
        ;;
 *)
        echo "仅支持如下参数: ${0} {one|two}"
        ;;
esac

循环语句

接下来我们学习一下shell中的循环语句

while do done 和 until do done

当你不确定循环要做多少次的时候,一般使用while循环,语法如下

python 复制代码
while [循环条件]
do 
		执行语句
done

shell中还有一种until循环,意思是 当满足制定条件时,终止循环;否则一直执行。

python 复制代码
until [循环终止条件]
do
	执行语句
done

while循环的例子,当输入不是yes也不是YES时,就执行循环

python 复制代码
[root@node4 bin]# vim while_yes.sh
#!/bin/bash
# while循环的例子
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
while [ "${yn}" != "yes" -a "${yn}" != "YES" ]
do
        read -p "请输入yes或YES来终止程序:" yn
done
echo "你输入了正确的选择" 

until循环的例子,当输入时yes或者YES时,就终止循环

python 复制代码
[root@node4 bin]# vim until_yes.sh
#!/bin/bash
# until循环的例子
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
until [ "${yn}" = "yes" -o "${yn}" = "YES" ]
do
        read -p "请输入yes或YES来终止程序:" yn
done
echo "你输入了正确的选择" 

for...do...done循环

已知要进行几次循环的情况下,一般使用for循环,基本语法如下

python 复制代码
for var in con1 con2 con3 ...
do
	执行程序
done

基本for循环例子:

python 复制代码
#!/bin/bash
# for循环的例子
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
for animal in dog cat neko
do
        echo "当前动物是:${animal}"
done

打印本机所有用户

python 复制代码
#!/bin/bash
# for循环打印用户
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
users=$(cut -d ':' -f1 /etc/passwd)
for user in ${users}
do
        echo "${user}"
done

检测本地哪些ip已占用

python 复制代码
#!/bin/bash
# for循环的例子3
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
network="192.168.32"
for ip in $(seq 1 20)
do
        ping -c 1 -w 1 ${network}.${ip} &> /dev/null && result=0 || result=1
        if [ "${result}" == 0 ]; then
                echo "IP ${network}.${ip} 在使用中"
        else
                echo "IP ${network}.${ip} 未占用" 
        fi
done

for...done的另一种写法

其实就类似其他语言的 for(i=0, i<10,i++)

python 复制代码
for (( 初始值; 限制值; 赋值运算))
do
	程序
done

我们计算一个累加,

python 复制代码
#!/bin/bash
# for循环的例子4
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "请输入一个整数,我将计算1+2+..到n的值:" num
s=0
for (( i=1; i<=${num}; i=i+1 ))
do
        s=$((${s}+${i}))
done
echo "1+2+...+${num}的计算结果是:${s}"

随机数RANDOM

很多时候我们需要产生一个随机数,这时可以使用random

python 复制代码
#!/bin/bash
# 随机数的例子
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
eat[1]="麦当劳"
eat[2]="KFC"
eat[3]="泡面"
eat[4]="螺蛳粉"
eatnum=4
check=$(( RANDOM % ${eatnum}  + 1 ))
echo "今天吃${eat[${check}]}"

Shell脚本的调试

执行shell脚本是时,如果有语法错误,除了在执行时报错之外,也可以提前进行调试。

python 复制代码
sh [-nvx] scripts.sh
参数如下:
-n 不执行脚本,仅查询语法问题
-v 执行脚本前,先输出内容
-x 输出执行脚本内容

比如:

python 复制代码
[root@node4 bin]# sh -x for_1.sh
+ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/root/bin
+ export PATH
+ for animal in dog cat neko
+ echo 当前动物是:dog
当前动物是:dog
+ for animal in dog cat neko
+ echo 当前动物是:cat
当前动物是:cat
+ for animal in dog cat neko
+ echo 当前动物是:neko
当前动物是:neko

小结

本章我们学习了shell脚本的基本语法,包括

用户交互、脚本的编写和执行、条件判断语句if和case、循环语句while和for以及使用sh -x 进行shell脚本的简单debug。

相关推荐
内核程序员kevin3 小时前
TCP Listen 队列详解与优化指南
linux·网络·tcp/ip
朝九晚五ฺ7 小时前
【Linux探索学习】第十四弹——进程优先级:深入理解操作系统中的进程优先级
linux·运维·学习
自由的dream7 小时前
Linux的桌面
linux
xiaozhiwise7 小时前
Makefile 之 自动化变量
linux
Kkooe8 小时前
GitLab|数据迁移
运维·服务器·git
猫爪笔记8 小时前
前端:HTML (学习笔记)【1】
前端·笔记·学习·html
久醉不在酒9 小时前
MySQL数据库运维及集群搭建
运维·数据库·mysql
pq113_69 小时前
ftdi_sio应用学习笔记 3 - GPIO
笔记·学习·ftdi_sio
澄澈i9 小时前
设计模式学习[8]---原型模式
学习·设计模式·原型模式