Skip to content

第一章 Shell脚本基础 v1.0

第一部分 Shell脚本简介

一、Shell种类与选择

  1. Bourne Shell(sh):Bourne Shell 是 Unix 系统中最早的 Shell 之一,它由 Stephen Bourne 开发,许多其他 Shell 都基于 Bourne Shell 的语法。它的特点是简洁、高效,并且在大多数类 Unix 系统中都有安装。Bourne Shell 脚本以 “.sh” 为常见的文件扩展名。它适用于编写简单、通用的脚本,对脚本的兼容性要求较高的场景。例如,一些系统启动脚本和基本的系统管理脚本常常使用 Bourne Shell 编写。
  2. Bash(Bourne Again Shell):Bash 是 Bourne Shell 的增强版本,它是大多数 Linux 发行版的默认 Shell。Bash 兼容 Bourne Shell 的语法,并在此基础上增加了许多实用的功能,如命令补全、命令历史、作业控制等。Bash 还支持更复杂的脚本编程,例如数组、函数、条件表达式等。它广泛应用于系统管理、自动化任务和日常用户操作中。对于需要编写功能丰富、交互性强的脚本的用户和系统管理员来说,Bash 是一个很好的选择。
  3. Z Shell(zsh):Z Shell 是一种功能强大且高度可定制的 Shell。它具有许多高级特性,如智能命令补全(包括对文件、命令、参数的智能提示)、拼写纠错、主题定制等。Z Shell 还支持一些高级编程特性,如数组和哈希表的高级操作。对于追求个性化和高效操作的用户,特别是那些需要频繁使用命令行的开发者和系统管理员,Z Shell 是一个非常有吸引力的选择。

二、Shell脚本基本结构

bash
#!/bin/bash    # 脚本第一行,指定解释器
# ...          # 脚本主体

Shell脚本主体中一般包括几个部分,简单示例如下:

bash
# 这是一行注释
bash
name="Mike"
bash
echo "Hello, $name!"
bash
if [ $a -eq $b ]; then
    echo "a等于b"
fi
bash
function hello() {
    echo "Hello, $name!"
}

三、注释与文档

注释和文档是编写Shell脚本的重要部分,有助于提高代码的可读性和可维护性。

bash
# 这是一行注释
bash
:<<'COMMENT'
这是一个多行注释
COMMENT

第二部分 基本语法

一、变量与常量

变量是存储数据的容器,常量在Shell中没有严格的定义,通常通过变量实现

bash
name="Mike"
bash
echo "Hello, $name"  # 适用于简单的变量引用,且后面没有其他字符
echo "Bye, ${name}"  # 更安全的写法,适用于任何情况尤其是变量跟有其他字符时

二、数据类型

Shell脚本中的数据类型较为简单,主要包括字符串、整数与数组三种。

bash
# 默认数据类型
str="string"
bash
num=10
bash
arr=("apple" "banana" "cherry")

三、字符串操作

Shell支持多种字符串操作,常用的如下

bash
str1="Hello"
str2="World"
result="$str1 $str2"  # Hello World
bash
str="Hello"
len=${#str}  # 5
bash
str="Hello, World!"
substr=${str:6:5}  # World
bash
str="Hello, World!"
new_str=${str//o/O}  # HellO, WOrld!

四、数字运算

Shell脚本支持基本的数字运算,但需要使用 expr 或 $((...))

bash
a=5
b=3
sum=$((a + b))       # 8  '+':加法
diff=$((a - b))      # 2  '-':减法
product=$((a * b))   # 15 '*':乘法
quotient=$((a / b))  # 1  '/':除法
remainder=$((a % b)) # 2  '%':取余

五、数组

Shell脚本支持数组,用于存储多个值

bash
arr=("apple" "banana" "cherry")
bash
echo ${arr[0]}  # apple
bash
len=${#arr[@]}  # 3
bash
for item in "${arr[@]}"; do
    echo $item
done

第三部分 输入输出

一、标准输入输出

Shell脚本通过标准输入(stdin)、标准输出(stdout)和标准错误输出(stderr)进行交互。

bash
read name  # 从键盘读取输入
bash
echo "Hello, $name!"  # 输出到终端
bash
echo "错误信息" >&2  # 输出到标准错误

二、文件读写操作

bash
# 全部读取
cat file.txt
# 逐行读取
while IFS= read -r line; do
    echo "$line"
done < file.txt
bash
echo "Hello, World!" > file.txt    # 覆盖写入
echo "Append content" >> file.txt  # 追加写入

三、管道与重定向

管道和重定向用于控制数据流

1.管道

将一个命令的输出作为另一个命令的输入

bash
echo "Hello, World!" | tr '[:lower:]' '[:upper:]'  # HELLO, WORLD!

2.重定向

bash
echo "Hello" > file.txt  # 覆盖写入
echo "World" >> file.txt  # 追加写入
bash
while IFS= read -r line; do
    echo $line
done < file.txt
bash
command 2> error.log  # 将错误信息写入error.log
bash
command &> output.log  # 将标准输出和错误输出合并到output.log

第四部分 控制结构

一、条件表达式

在Shell脚本中,条件表达式用于在 if、while、until 等控制结构中进行条件判断

1.一般比较

bash
"$a" -eq "$b"  # 判断a等于b
"$a" -ne "$b"  # 判断a不等于b
"$a" -lt "$b"  # 判断a小于b
"$a" -le "$b"  # 判断a小于或等于b
"$a" -gt "$b"  # 判断a大于b
"$a" -ge "$b"  # 判断a大于或等于b
bash
"$a" = "$b"   # 判断字符串相等
"$a" != "$b"  # 判断字符串不相等
-z "$a"       # 判断字符串长度为零
-n "$a"       # 字符串长度非零

2.文件条件

bash
-e "path/to/file" # 指定文件存在
-f "path/to/file" # 指定文件存在且为普通文件
-d "path/to/file" # 指定文件存在且为目录
-r "path/to/file" # 指定文件存在且可读
-w "path/to/file" # 指定文件存在且可写
-x "path/to/file" # 指定文件存在且可执行
-s "path/to/file" # 指定文件存在且大小大于零

3.高级判断

[[ ]] 是 Bash 的扩展条件测试命令,支持更多的特性,如模式匹配和正则表达式。

bash
## 字符串比较
# 判断字符串a小于b
[[ "$a" < "$b" ]]  
# 判断字符串a大于b
[[ "$a" > "$b" ]]  

## 字符串匹配 (注意匹配时模式不要加引号,否则会当做普通字符处理)
# 简单匹配,支持 *(匹配任意长度任意字符)、?(匹配单个任意字符)、[...](匹配括号中的任意一个字符) 等通配符
[[ "$a" == $exp ]]
# 正则表达式匹配,支持完整正则表达式匹配
[[ "$a" =~ $exp ]]

二、条件结构

1.if语句

bash
if [ 条件表达式1 ]; then
  # 当条件1为真时执行的命令
elif [ 条件表达式2 ]; then
  # 当条件2为真时执行的命令
else
  # 当所有条件都为假时执行的命令
fi
bash
number=0
if [ $number -eq 0 ];then  
  echo "number = 0"        # 输出
else 
  echo "number != 0"       # 未输出
fi

2.case语句

bash
case $variable in
  pattern1)
    # 当变量匹配 pattern1 时执行的命令
    ;;
  pattern2)
    # 当变量匹配 pattern2 时执行的命令
    ;;
  *)
    # 当变量不匹配任何模式时执行的命令
    ;;
esac
bash
read -p "Enter a number: " number
case $number in
  1)
    echo "You entered one."
    ;;
  2)
    echo "You entered two."
    ;;
  3)
    echo "You entered three."
    ;;
  *)
    echo "You entered a number other than 1, 2, or 3."
    ;;
esac

三、循环结构

1.for循环

bash
for var in list; do  # list中元素以空格分隔
  # 对于列表中的每个元素执行的命令
done
bash
list="1 2 3 4 5"

for i in $list; do
  echo "Number: $i"
done
# 依次输出: Number: 1 ~ Number: 5

for i in "$list"; do
  echo "Number: $i"
done
# 输出: Number: 1 2 3 4 5

2.while循环

先判断条件表达式的值,当条件为真时执行循环体,然后再次判断条件

bash
while [ condition ]; do
  # 当条件为真时执行的命令
done
bash
count=1
while [ $count -le 5 ]; do
  echo "Count: $count"
  count=$((count + 1))
done

3.until循环

先执行循环体,然后判断条件表达式的值,当条件为假时继续执行循环体,直到条件为真时结束循环

bash
until [ condition ]; do
  # 先执行一次循环体,当条件为假时继续执行
done
bash
count=1
until [ $count -gt 5 ]; do
  echo "Count: $count"
  count=$((count + 1))
done