Contents

Shell脚本

简介

Shell 脚本是一种解释型语言(区别于 C 语言等编译型语言),本质上是 Shell 命令的有序集合。

基本步骤

  1. 创建脚本文件
1
2
3
vi filename.sh
# 输入命令,例如
date
  1. 赋予执行权限
1
2
3
chmod 740 filename.sh
# 或者
chmod u+x filename.sh
  1. 执行脚本
1
./filename.sh

Shell 变量

注意:等号两边不能加空格;变量名通常使用全大写。

用户自定义变量

支持创建变量存储数据,但不支持数据类型,任何赋给变量的值都是字符串。

1
2
COUNT=1
echo ${COUNT}

只读变量与删除

1
2
3
4
5
6
# 设置只读变量
myUrl="https://www.google.com"
readonly myUrl

# 删除变量
unset COUNT

字符串

  • 单引号:原样输出
  • 双引号:支持变量和转义字符
1
2
3
4
5
# 获取字符串长度
echo ${#myUrl}

# 截取字符串(下标从 0 开始)
echo ${myUrl:1:4}

数组

Shell 只支持一维数组,元素类型可以不一致,可使用非连续下标。

1
2
3
array_name=(value0 value1 value2 value3)
echo ${array_name[0]}     # 获取第一个元素
echo ${array_name[@]}     # 获取所有元素

关联数组

1
2
3
4
5
#!/bin/bash
declare -A site    # 定义关联数组(键值形式)
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"

位置变量

变量 说明
$# 参数个数
$@ 所有参数(参数分开)
$* 所有参数(作为整体)
$? 前一个命令的执行结果
$$ 当前进程的 ID

预定义变量

按位置传递参数:${0}${1}、…

创建 test.sh

1
2
3
4
5
6
#!/bin/bash
echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";

执行:

1
2
3
4
5
6
7
$ chmod +x test.sh
$ ./test.sh 1 2 3
Shell 传递参数实例!
执行的文件名:./test.sh
第一个参数为:1
第二个参数为:2
第三个参数为:3

环境变量

1
2
3
4
PATH=...
HOME=...
IFS=...
TERM=...

Shell 语句

Shell 有三大语句类型:说明性语句、功能性语句、结构性语句。

说明性语句

用于注释,不被执行。

1
2
3
4
5
# 单行注释

:<<EOF
多行注释内容...
EOF

功能性语句

读取输入

1
read var

从标准输入读取一行赋值给变量;若标准输入无数据,则等待数据到来或程序终止。支持类似 Python 的解包形式:read var1 var2 var3

算术运算

1
expr \( 10 + 10 \) \* 2

结果为 40。注意:需用反斜杠转义,运算符之间要有空格,表达式用反引号包围。

测试语句

test 命令用于检查条件是否成立,支持数值、字符和文件三种测试。

注意:返回 0 表示条件成立(true),返回非 0 表示条件不成立(false)

1
2
3
test "$answer" = "yes"     # 检查变量是否为字符串 yes
test $num -eq 18            # 检查变量是否等于 18
test -d tmp                 # 检查 tmp 是否为目录

数值测试

运算符 说明
-eq 等于(equal)
-ne 不等于(not equal)
-gt 大于(greater than)
-ge 大于等于(greater or equal)
-lt 小于(less than)
-le 小于等于(less or equal)

字符测试

运算符 说明
= 相等(仅一个等号)
!= 不相等
-z 字符串 字符串长度为 0
-n 字符串 字符串长度不为 0

文件测试

1
2
3
-e 文件名    # 文件存在
-r 文件名    # 文件存在且可读
# ... 还有更多文件测试选项

布尔运算符

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
!        # 非
-a       # 与(and)
-o       # 或(or)

# 示例
[ ! False ]                          # 非
[ $a -lt 20 -a $b -gt 100 ]          # 与,等同于 [[ $a -lt 20 && $b -gt 100 ]]
[ $a -lt 20 -o $b -gt 100 ]          # 或,等同于 [[ $a -lt 20 || $b -gt 100 ]]

# 优先级:! > -a > -o

输出命令

1
2
echo "..." > myfile          # 输出重定向到文件
somecommand > /dev/null      # 将输出丢弃到空设备

printfecho 类似,但更适合格式化输出:

1
2
printf "%s\n" "Hello, World!"
# %s 是字符串格式符,\n 表示换行

结构性语句

分支语句

if 条件语句

常与 test 命令结合使用:

1
2
3
4
5
6
7
#!/bin/bash
if [ $# -ne 1 ]    # 参数个数不等于 1 时执行 then
# 等同于 if (( $# -ne 1 ))
then
    echo "usage : $0 filename"
    exit
fi

配合 else、elif:

1
2
3
4
5
6
7
8
9
if condition1
then
    command1
elif condition2
then
    command2
else
    commandN
fi

case 多路分支

类似 switch-case:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/bash
echo -n "please choose (yes|no)?"
read R
case $R in    # 注意 in
    yes | Yes | y | Y | YES)
        echo "yes"
        ;;    # 类似 break
    no | No | n | N | NO)
        echo "no"
        ;;
    *)
        echo "wrong"
esac

循环语句

for 循环

1
2
3
4
5
6
#!/bin/bash
for I in `seq 1 2 10`
do
    echo "$I"
done
# 输出 1 到 10,步长为 2

while 循环(条件为时停止)

示例:输入 5 个数,判断等级:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash

I=0
while [ $I -lt 5 ]
do
    I=`expr $I + 1`
    echo -n "input score:"
    read S

    case `expr $S / 10` in
        9 | 10)
            echo "A"
            ;;
        6 | 7 | 8)
            echo "B"
            ;;
        *)
            echo "C"
            ;;
    esac
done

until 循环(条件为时停止)

循环控制

1
2
break n      # 跳出 n 层循环
continue n   # 跳转到最近 n 层循环的下一轮

示例:遇到偶数跳出循环:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/bin/bash

if [ $# -ne 5 ]
then
    echo "argument 5"
    exit
fi

for I
do
    if [ `expr $I % 2` -eq 1 ]
    then
        echo "$I"
    else
        break
    fi
done

Shell 函数

定义格式

函数必须放在脚本开始部分定义:

1
2
3
4
5
[function] 函数名 [()]
{
    action;
    [return int;]
}

函数调用后会返回退出状态码,可通过 $? 获取:

1
2
funWithReturn
echo "执行上述函数的输出为 $?"

传递函数返回值

1
value_name=`function_name [arg1 arg2 ...]`

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
grep_user()
{
    R=`grep "$1" /etc/passwd | wc -l`
    echo $R
}

echo -n "input username:"
read USER
RET=`grep_user $USER`
echo "----return $RET----"
if [ $USER -eq 1 ]
then
    echo "$USER exist"
else
    echo "$USER not exist"
fi

获取函数返回状态

1
2
function_name [arg1 arg2 ...]
echo $?

变量作用域

  • 全局作用域:脚本任何地方都可访问
  • 局部作用域:仅在声明变量的作用域内访问
1
local variable_name=value

其他

Shell 脚本可以包含其他脚本,被包含的文件不需要可执行权限,只需在调用脚本中使用:

1
source 被调用的脚本文件名

拓展与后续学习建议

  • 正则表达式:学习基础正则和扩展正则,用于模式匹配和文本处理
  • sed 流编辑器:实现自动化文本替换、删除、插入等操作
  • awk 文本处理:强大的文本分析工具,适合处理结构化数据
  • 函数封装技巧:学习函数的参数校验、错误处理和返回值设计
  • 脚本调试方法:学习 bash -x 脚本名 调试模式,掌握 set -x、set -e 等调试选项
  • 实际项目练习:尝试编写服务监控脚本、自动备份脚本等实用工具