Shell 脚本

1. Shell 概述

Shell 是用户与操作系统内核之间的接口,它接收用户输入的命令并将其传递给操作系统执行。而 Shell 脚本就是一系列命令的集合,将这些命令按照一定的逻辑顺序编写在一个文本文件中,就形成了一个 Shell 脚本。通过执行这个脚本文件,系统会依次执行其中的命令,从而完成特定的任务。

  • 自动化任务:可以编写脚本自动完成一些重复性的工作,如文件备份、系统更新、日志清理等。例如,每天定时备份重要文件到指定的存储位置。
  • 系统管理:用于管理系统资源,如监控系统性能、创建和管理用户账户、配置网络等。
  • 软件安装与部署:可以编写脚本来自动化软件的安装过程,确保在不同的环境中都能一致地安装和配置软件。

2. 用户和硬件之间的关系

3. 脚本区别和环境确定

针对于环境变量, 在 Linux 中有两个相关的脚本文件

  • 系统配置脚本文件 /etc/profile
    • 针对于整个 Linux 系统的相关配置文件,如果修改之后,可以利用重启当前 Linux 系统操作完成相关配置生效。针对与普通用户基本无需操作
  • 当前用户的配置脚本文件 ~/.bashrc
    • 针对于每一个用户,在各自的家目录下都有一个 .bashrc 脚本环境配置文件,当前配置文件有且只针对于当前用户有效,其他用户无效,同时当前脚本的作用不会影响整个操作系统。

确认当前系统采用的脚步解析方式/脚本执行方式

echo $SHELL

执行效果

当前 Linux 解析执行脚本默认的方式为 bash 方式。

4. 和脚本见一面

脚本案例

#!/bin/bash  
# 脚本注释 
echo "Hello World!"
echo "你好,世界"

num=100
echo "num is $num"

read str
echo "str is $str"
# 脚本中文支持一般,通常情况下,不会在脚本中出现中文字符。

5. 脚本执行方式

脚本执行方式常用有三种

  • 脚本文件直接执行,需要当前脚本具备可执行权限。

    chmod 775 XXX.sh
    chmod +x xxx.sh
    # 以上两种方式都可以加入可执行权限
    
  • 利用 bashsh 执行当前文件

    bash xxx.sh
    
  • . xxx.sh 利用 . + 空格执行目标脚本文件

    . xxx.sh
    

6. shell 自定义变量

直接自定义变量操作

#!/bin/bash
num=100 
# 定义了一个变量 num 同时完成赋值操作 100
# 【注意】 = 两端不得有空格,否则 shell 语法报错

# 如果需要展示打印对应的变量存储数据,需要利用 $variable
echo "num is $num"
echo num is $num

num=200

echo "num is $num"
echo num is $num

read 定义变量,且从终端直接获取用户输入的数据内容

echo "Please input string : "
read str # 从键盘上获取用户输入的数据内容,存储到 str 变量中
echo "str value : $str"

export 变量,是将目标变量【临时】作为当前终端的环境变量,当前终端其他的程序可以使用。如果当前终端关闭,或者在其他终端中无法使用 export 临时生效的环境变量

04-export_var.sh

#!/bin/bash
export_var=2000
export export_var

05-var_test.sh

#!/bin/bash
echo "export_var is $export_var"

生效方式

  • 执行 04-export_var.sh,. 04-export_var.sh或者直接 source 04-export_var.sh
  • 执行 05-var_test.sh 可以看到临时环境变量 export_var

7. 系统预设环境变量

以下为常用的系统预设环境变量

#!/bin/bash
echo "当前用户的家目录为 : $HOME"
echo "当前工作目录为 : $PWD"
echo "当前登录的用户名为 : $LOGNAME"
echo "当前 Linux 默认脚本解析工具 : $SHELL"
echo "当前主机名 : $HOSTNAME"

8. $预设变量

$n:n 是一个整数,获取终端执行脚本的
$* : 获取当前执行脚本所有的参数内容
$$ : 当前脚本对应的系统 PID 进程号
$? : 获取上一条指令执行是否成功,成功返回 0 ,失败返回 1

#!/bin/bash
# 授予当前脚本文件权限之后执行。
# ./07-$var.sh aaa bbb ccc ddd 
# 整个脚本执行对应的参数个数为 5 个
# 对应的下标范围是 0 ~ 4
# 下标 0  ==> ./07-$var.sh 下标 2 ==> bbb
echo "当前脚本文件名称为 : $0"

echo "当前脚本执行第一个参数: $1"
echo "当前脚本执行第二个参数: $2"
echo "当前脚本执行第三个参数: $3"
echo "当前脚本执行第四个参数: $4"

echo "当前脚本执行所有参数: $*" # 会获取所有的参数,但是不包括 $0

echo "当前脚本执行对应的程序 PID(Process ID)/进程号 $$"

echo "-----------------------------------------------"

ls -l
echo "之前的命令是否执行成功 成功(0) 失败/错误(1): $?"

cd /root
echo "之前的命令是否执行成功 成功(0) 失败/错误(1): $?"

9. 脚本变量的特殊用法

"" 字符串内部变量会被解释
'' 整个内容当做字符串处理,不会解释其中的变量内容
转义字符 转义字符如果需要生效,必须有对应的 shell 脚本参数 -e
() 内部修改外部定义的变量,对于外部无影响
{} 内部修改外部定义的变量,对于外部有影响

#!/bin/bash

num=100

echo "num is $num" # 当前使用的是双引号,内部的变量数据会被 shell 解释
echo 'num is $num' # 整个文本都是当做字符串数据进行处理

echo "date : `date`" # 解释当前 Shell 预设的变量 date 

# 转义字符 \n \t \\ \" \' \a
# 如果需要转义字符串生效,必须使用 -e shell 脚本操作
echo -e "Text \n Text"
echo -e "\a"

# () {} 的区别
ret=200
# 小括号中修改的外部变量内容,有且只在小括号内部有效,对于外部数据无效
(
    ret=300
    echo "ret : $ret"

    ymc=150
)
echo "ret : $ret"

# 大括号修饰的变量内容,内外都有效
yqc=200
{
    yqc=300
    echo "yqc : $yqc"
    yqm=100
}
echo "yqc : $yqc"

echo $ymc + $yqm # ?

命令作为参数时需要加``而不是单引号

10. 条件判断

两种方式

  • test 标记 内容
  • [ 标记 内容 ] 要求中括号内部到判断语句必须有空格

注:test -n命令是字符串的长度不为零则为真

test -n没有参数时,默认返回 0(真)

10.1 文件

在文件操作中使用的判断标记

-e 是否存在 -d 是目录 -f 是文件
-r 可读 -w 可写 -x 可执行
-L 符号连接 -c 是否字符设备 -b 是否块设备
-s 文件非空,如果文件为空,$? 为 1 ,如果文件不为空,$? 为 0
#!/bin/bash

test -e /home # 判断指定路径文件或者文件夹是否存在
echo $?   # 获取上一条指令执行是否成功,成功返回 0 ,失败返回 1

test -e /home/qq # 判断指定路径文件或者文件夹是否存在
echo $?

test -d ~
echo $?

test -f ~
echo $?

test -f ~/.bashrc
echo $?

[ -r ./01-test.sh ]
echo $?

[ -w ./01-test.sh ]
echo $?

[ -x ./01-test.sh ]
echo $?

[ -s ../02/1.txt ]
echo $?
10.2 字符串

字符串判断使用的判断标记

= 两个字符串相等 
!= 两个字符串不相等
-z 空串 
-n 非空串
#!/bin/bash

str1="ABC"
str2="ABC"

test $str1=$str2
echo $?

[ "123" != "234" ]
echo $?

[ "123" != "123" ]
echo $?

echo "----------------------------------------------"


# [ -z $str1 ] # 
[ -z $str10 ]
echo $?

str3="123"
[ -z $str3 ]
echo $?


str5=""
[ -n "$str5" ]
echo $?
10.3 数据

数据判断使用的判断标记

-eq # equal 数值相等   
-ne # not equal 数值不相等
-gt # greater than 数 1 大于数 2 
-ge # greater equal 数 1 大于等于数 2
-le # less equal 数 1 小于等于数 2
-lt # less than 数 1 小于数 2
#!/bin/bash

num=10

[ $num -eq 10 ]
echo $?

[ $num -ne 10 ]
echo $?

[ $num -gt 8 ]
echo $?

[ $num -lt 8 ]
echo $?

[ $num -le 10 ]
echo $?

[ $num -ge 10 ]
echo $?

10.4 组合判断/复合判断

组合判断

&&:
command1 && command2
&&左边命令(command1)执行成功(即返回 0)shell才执行&&右边的命令(command2)

||
||左边的命令(command1)未执行成功(即返回非 0)shell才执行||右边的命令(command2)

逻辑判断

条件 作用
-a 类似于逻辑与操作
-o 类似于逻辑或操作
! 类似于取反操作
#!/bin/bash

num1=10
num2=20

# && 第一个条件如果成立,&& 符号之后的内容执行
[ $num2 -gt $num1 ] && echo "&& 测试"

# || 第一个条件不成立,|| 符号之后的内容执行
[ $num2 -lt $num1 ] || echo "|| 测试"

echo "----------------------------------------------------"

num3=100
num5=200

test $num1 -lt $num2 -a $num3 -lt $num5
echo $?

[ $num1 -lt $num2 -a $num3 -lt $num5 ]
echo $?

[ 10 -gt 15 -o 10 -gt 15 ]
echo $?

[ ! 10 -gt 5 ]
echo $?

declare -i num
read num

11. 控制语句

11.1 if 分支结构

格式有三种

if [ 条件判断 ]; then
	# 处理方式
fi # finish if
if [ 条件判断 ]; then
	# if 处理方式
else
	# else 处理方式
fi # finish if
if [ 条件判断 1 ]; then
	# if 处理方式
elif [ 条件判断 2 ]; then
	# elif 处理方式
else
	# else 处理方式
fi # finish if

案例代码

#!/bin/bash

echo "请输入一个整数 : "
read num

if [ $num -gt 10 ]; then
    echo "用户输入数据大于 10"
fi

echo "---------------------------------------------"

echo "请输入一个整数 : "
read num1

if [ $num1 -gt 20 ]; then
    echo "用户输入数据大于 20"
else
    echo "用户输入数据不大于 20"
fi

echo "---------------------------------------------"

echo "请输入一个整数 : "
read num3

if [ $num3 -ge 100 ]; then
    echo "送闫某昌一位"
elif [ $num3 -ge 90 ]; then
    echo "送某齐昌一位"
else
    echo "送闫齐某一位"
fi
11.2 case 控制语句

格式

case $变量 in
	"第一个选项")
		处理方式
		;;
	"第二个选项")
		处理方式
		;;
	*)
		最终处理方式
		;;
esac
#!/bin/bash

# 需要授予权限执行当前脚本程序
echo "脚本执行第一个参数内容 : "

# $1 是获取第一个参数
case $1 in
    "闫")
        echo "喜提研究僧"
        ;;
    "齐")
        echo "刚果金社区幼儿园"
        ;;
    "昌")
        echo "育儿班"
        ;;
    *)
        echo "长江大学"
        ;;
esac
11.3 for 循环

格式

for (( 初始化; 限制值; 步进关系 ))
do
	执行语句
done

案例代码

#!/bin/bash
declare -i sum
for (( i=1; i<=100; i=i+1 ))
do
    sum=sum+i
done

echo "Sum : $sum"

echo "-------------------------------------"

# 当前循环 i 在 in 之后的数据范围以内,并且逐一遍历
for i in 1 2 3 4 5 6 7 8 9 
do  
    echo $i
done

echo "-------------------------------------"

# `ls` 是执行目标 Linux 命令,获取当前文件列表
# 循环是将文件列表中的每一个文件或者文件夹名称获取
# if 分支完成对应的分类处理操作
for file_name in `ls`
do
    if [ -f $file_name ]; then
        echo "$file_name is File"
    elif [ -d $file_name ]; then
        echo "$file_name is Dir"
    fi 
done
11.4 while 循环 和 until 循环

格式

while [ condition ] 
do
	执行语句
done

格式

until [ condition ]
do
	执行语句
done
  • while 循环控制是条件满足时,代码执行语句可以运行
  • until 循环控制是条件不满足是,执行对应的语句,【直到】条件满足终止
#!/bin/bash
declare -i begin
declare -i end

begin=1
end=10

while [ $begin -lt $end ]  # 执行
do 
    begin=begin+1
    echo "while 循环结构执行中..."
done

echo "-------------------------------------------------------"

begin=1
end=10

until [ $begin -lt $end ]  # 不执行
do
	begin=begin+1
    echo "until 循环结构执行中..."
done

echo "-------------------------------------------------------"

begin=1
end=10

while [ $begin -lt $end ]  # 执行
do
    begin=begin+1
    echo "while 循环结构执行中..."
done

echo "-------------------------------------------------------"

begin=1
end=10

until [ $begin -gt $end ] # 执行
do
	begin=begin+1
    echo "until 循环结构执行中..."
done
11.5 break 和 continue

有且只允许操作和 C/C++ 一致

12. 函数

格式

  • 函数和 C/C++ 一样,都要求有对应的返回值类型,函数名和参数类型,但是 shell 中对于返回和参数类型非常的【自由】
  • 函数参数取决于用户提供的参数情况,且按照和执行脚本一样的参数获取方式进行提取操作。脚本执行的参数是 ​0 1 ​2.... 1 对应函数中第一个参数
  • 返回值取决于用户在函数内部进行的数据反馈
函数名 ()
{

}

function 函数名()
{

}

案例代码

#!/bin/bash

# 函数声明 +  实现
is_dir()
{
    # 判断当前用户提供的参数个数小于 1,直接返回 1,错误状态
    if [ $# -lt 1 ]; then
        return 1
    fi
    # 如果当前第一个参数路径对应的是一个文件夹,返回 2
    if [ -d $1 ]; then  
        return 2
    else
        return 3 # 普通文件返回 3
    fi
}

echo "请输入路径 : "
read filepath

# 函数调用,需要提供函数名和对应的参数,不需要小括号
# is_dir 函数名 $filepath 是函数参数,对应 $1
is_dir $filepath
echo "函数运行结果 :  $?"