第六章 Shell脚本 v1.0
第一部分 Shell脚本简介
一、Shell脚本基本结构
#!/bin/bash # 脚本第一行,指定解释器
# ... # 脚本主体Shell脚本主体中一般包括几个部分,简单示例如下:
# 这是一行注释name="Mike"echo "Hello, $name!"if [ $a -eq $b ]; then
echo "a等于b"
fifunction hello() {
echo "Hello, $name!"
}二、注释与文档
注释和文档是编写Shell脚本的重要部分,有助于提高代码的可读性和可维护性。
# 这是一行注释:<<'COMMENT'
这是一个多行注释
COMMENT第二部分 基本语法
一、变量与常量
变量是存储数据的容器,常量在Shell中没有严格的定义,通常通过变量实现
name="Mike"echo "Hello, $name" # 适用于简单的变量引用,且后面没有其他字符
echo "Bye, ${name}" # 更安全的写法,适用于任何情况尤其是变量跟有其他字符时二、数据类型
Shell脚本中的数据类型较为简单,主要包括字符串、整数与数组三种。
# 默认数据类型
str="string"num=10arr=("apple" "banana" "cherry")三、字符串操作
Shell支持多种字符串操作,常用的如下
str1="Hello"
str2="World"
result="$str1 $str2" # Hello Worldstr="Hello"
len=${#str} # 5str="Hello, World!"
substr=${str:7:5} # Worldstr="Hello, World!"
new_str=${str//o/O} # HellO, WOrld!四、数字运算
Shell脚本支持基本的数字运算,但需要使用 expr 或 $((...))
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脚本支持数组,用于存储多个值
arr=("apple" "banana" "cherry")echo ${arr[0]} # applelen=${#arr[@]} # 3for item in "${arr[@]}"; do
echo $item
done第三部分 输入输出
一、标准输入输出
Shell脚本通过标准输入(stdin)、标准输出(stdout)和标准错误输出(stderr)进行交互。
read name # 从键盘读取输入echo "Hello, $name!" # 输出到终端echo "错误信息" >&2 # 输出到标准错误二、文件读写操作
# 全部读取
cat file.txt
# 逐行读取
while IFS= read -r line; do
echo "$line"
done < file.txtecho "Hello, World!" > file.txt # 覆盖写入
echo "Append content" >> file.txt # 追加写入三、管道与重定向
管道和重定向用于控制数据流
1.管道
将一个命令的输出作为另一个命令的输入
echo "Hello, World!" | tr '[:lower:]' '[:upper:]' # HELLO, WORLD!2.重定向
echo "Hello" > file.txt # 覆盖写入
echo "World" >> file.txt # 追加写入while IFS= read -r line; do
echo $line
done < file.txtcommand 2> error.log # 将错误信息写入error.logcommand &> output.log # 将标准输出和错误输出合并到output.log第四部分 控制结构
一、条件表达式
在Shell脚本中,条件表达式用于在 if、while、until 等控制结构中进行条件判断
1.一般比较
"$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"$a" = "$b" # 判断字符串相等
"$a" != "$b" # 判断字符串不相等
-z "$a" # 判断字符串长度为零
-n "$a" # 字符串长度非零2.文件条件
-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 的扩展条件测试命令,支持更多的特性,如模式匹配和正则表达式。
# 字符串比较
# 判断字符串a小于b
[[ "$a" < "$b" ]]
# 判断字符串a大于b
[[ "$a" > "$b" ]]
# 字符串匹配 (注意匹配时模式不要加引号,否则会当做普通字符处理)
# 简单匹配,支持 *(匹配任意长度任意字符)、?(匹配单个任意字符)、[...](匹配括号中的任意一个字符) 等通配符
[[ "$a" == $exp ]]
# 正则表达式匹配,支持完整正则表达式匹配
[[ "$a" =~ $exp ]]二、条件结构
1.if语句
if [ 条件表达式1 ]; then
# 当条件1为真时执行的命令
elif [ 条件表达式2 ]; then
# 当条件2为真时执行的命令
else
# 当所有条件都为假时执行的命令
finumber=0
if [ $number -eq 0 ];then
echo "number = 0" # 输出
else
echo "number != 0" # 未输出
fi2.case语句
case $variable in
pattern1)
# 当变量匹配 pattern1 时执行的命令
;;
pattern2)
# 当变量匹配 pattern2 时执行的命令
;;
*)
# 当变量不匹配任何模式时执行的命令
;;
esacread -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循环
for var in list; do # list中元素以空格分隔
# 对于列表中的每个元素执行的命令
donelist="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 52.while循环
先判断条件表达式的值,当条件为真时执行循环体,然后再次判断条件
while [ condition ]; do
# 当条件为真时执行的命令
donecount=1
while [ $count -le 5 ]; do
echo "Count: $count"
count=$((count + 1))
done3.until循环
先执行循环体,然后判断条件表达式的值,当条件为假时继续执行循环体,直到条件为真时结束循环
until [ condition ]; do
# 先执行一次循环体,当条件为假时继续执行
donecount=1
until [ $count -gt 5 ]; do
echo "Count: $count"
count=$((count + 1))
done第五部分 函数
一、函数定义
function function_name {
# 函数体
command1
command2
...
}function_name() {
# 函数体
command1
command2
...
}二、函数调用
定义好函数后,可以在脚本的其他地方直接通过函数名调用
function_name [参数]三、函数参数
函数可以接受参数,这些参数在函数内部通过位置变量 $1, $2, ..., $n 来访问,其中 $1 表示第一个参数,$2 表示第二个参数,依此类推。在Shell脚本中,特殊变量 $0 表示脚本本身的名称,而不是函数名。
# 带参数的函数示例
hello() {
echo "Hello, $1! Welcome to $2."
}
# 调用函数并传递参数
hello "Alice" "Shell Programming"四、函数返回值
Shell中返回值一般指函数的退出状态码,用于表示函数执行的成功(0)或失败(非0)。
默认情况下,函数的退出状态码是最后一条命令的退出状态码。可以使用 return 语句显式设置退出状态码。
function check_number() {
if [ $1 -lt 0 ]; then
echo "Error: Number is negative"
return 1 # 函数返回状态码1
fi
echo "Number is positive"
return 0 # 函数返回状态码0
}check_number -5
echo $? # 输出:1
check_number 5
echo $? # 输出:0另一种概念的返回值是输出返回值,函数可以通过
echo返回值,调用时可以捕获这些输出。
calculate_sum() {
local sum=$(( $1 + $2 ))
echo $sum
}
# 捕获函数的输出
result=$(calculate_sum 5 3)
echo "The sum is: $result" # 输出:The sum is: 8五、变量作用域
Shell中,变量的作用域有全局和局部之分
- 全局变量:在函数外部定义的变量或在函数内部未使用 local 关键字声明的变量是全局变量,可以在函数内外访问
- 局部变量:在函数内部使用 local 关键字声明的变量是局部变量,仅在函数内部可见
global_var=10
my_function() {
local local_var=20 # 局部变量
echo "Inside function: global_var=$global_var, local_var=$local_var"
# Inside function: global_var=10, local_var=20
}
my_function
echo "Outside function: global_var=$global_var, local_var=$local_var"
# Outside function: global_var=10, local_var=第六部分 文本处理
一、grep
用于在文本中搜索匹配特定模式的行,并将匹配的行打印出来
grep [选项] '搜索词' 文件
# 常用选项
# -v:反转匹配,显示不符合模式的行
# -E:使用扩展正则表达式
# -i:忽略大小写
# -w:全词匹配
# -n:显示匹配行的行号
# -r:递归搜索目录中的文件grep "a" example.txt # 搜索 example.txt 文件中含有a的行
grep -i "a" example.txt # 搜索 example.txt 文件中包含 "a" 且忽略大小写的行
grep -v "a" example.txt # 搜索 example.txt 文件中不包含 "a" 的行
grep -r 'hello' . # 递归搜索当前目录下所有文件中包含 "hello" 的行
grep -E 'apple|banana' example.txt # 使用扩展正则表达式搜索包含 "apple" 或 "banana" 的行二、sed
流编辑器,能够在不修改原始文件的情况下,对文本进行快速高效的编辑操作
sed [选项] '命令' 文件
# 常用选项
# -n:默认情况下,sed会输出处理后的所有行,-n选项只输出经过sed命令处理的行
# -e:允许在同一命令行中执行多个sed命令
# -i:直接修改文件内容(修改前建议备份文件)
# -f:从文件中读取sed命令sed 's/apple/orange/g' example.txt # 将example.txt文件中每一行的apple替换为orange
sed -n '2p' example.txt # 只打印example.txt文件的第2行
sed '1,3d' example.txt # 删除example.txt文件中第1到第3行
sed 's/^/#/g' example.txt # 在example.txt文件每一行开头添加#
sed -i's/old/new/g' example.txt # 直接修改example.txt文件,将old替换为new三、awk
文本处理工具,能够逐行处理文本,通过指定的模式匹配和操作对数据进行提取、转换和输出
awk [选项] '模式 {操作}' 文件
# 常用选项
# -F:指定输入字段分隔符
# -v:定义变量
# 常用模式
# BEGIN:在处理文件内容之前执行一次操作
# END:在处理完文件所有内容之后执行一次操作
# 常用操作
# $1、$2...:表示第1个、第2个...字段
# NF:表示当前行的字段数量
# NR:表示当前处理的行号awk -F: '{print $1}' /etc/passwd # 以:为分隔符,打印/etc/passwd文件中的第1个字段(用户名)
awk '{print $NF}' example.txt # 打印example.txt文件中每一行的最后一个字段
awk 'NR==10' example.txt # 打印example.txt文件的第10行
awk -v sum=0 '{sum+=$1} END {print sum}' data.txt # 计算data.txt文件中第一列数字的总和
awk 'BEGIN {FS=","; OFS=":"} {print $1,$2}' data.csv # 以逗号为输入分隔符,冒号为输出分隔符,打印data.csv文件的前两列