30天Shell脚本编程实战
Day 1: 基础脚本 - Hello World
bash
#!/bin/bash
# 第一个Shell脚本,输出Hello World
# 输出文本到终端
echo "Hello, World!"
# 输出当前日期
date
# 显示当前工作目录
pwd
运行结果:
Hello, World!
Tue Dec 25 10:00:00 UTC 2024
/home/user
Day 2: 变量和用户输入
bash
#!/bin/bash
# 变量使用和用户输入示例
# 提示用户输入姓名
read -p "请输入你的名字: " username
# 提示用户输入年龄
read -p "请输入你的年龄: " age
# 使用变量
echo "你好, $username!"
echo "你今年 $age 岁"
# 计算出生年份
current_year=$(date +%Y)
birth_year=$((current_year - age))
echo "你的出生年份大约是 $birth_year 年"
# 显示变量信息
echo "用户名变量类型: ${username@a}"
echo "年龄变量值: $age"
运行示例:
请输入你的名字: 张三
请输入你的年龄: 25
你好, 张三!
你今年 25 岁
你的出生年份大约是 1999 年
用户名变量类型:
年龄变量值: 25
Day 3: 数学运算
bash
#!/bin/bash
# 数学运算示例
echo "=== 基础数学运算 ==="
# 定义变量
num1=15
num2=4
# 加法
sum=$((num1 + num2))
echo "$num1 + $num2 = $sum"
# 减法
diff=$((num1 - num2))
echo "$num1 - $num2 = $diff"
# 乘法
prod=$((num1 * num2))
echo "$num1 * $num2 = $prod"
# 除法(整数)
div=$((num1 / num2))
echo "$num1 / $num2 = $div (整数除法)"
# 取余
mod=$((num1 % num2))
echo "$num1 % $num2 = $mod"
# 指数运算
power=$((num1 ** 2))
echo "$num1 ^ 2 = $power"
echo -e "\n=== 浮点数运算 ==="
# 使用bc进行浮点运算
float_div=$(echo "scale=2; $num1 / $num2" | bc)
echo "$num1 / $num2 = $float_div (浮点除法)"
# 平方根
sqrt=$(echo "scale=2; sqrt($num1)" | bc -l)
echo "√$num1 ≈ $sqrt"
echo -e "\n=== 递增递减 ==="
counter=5
echo "初始值: $counter"
((counter++))
echo "递增后: $counter"
((counter--))
echo "递减后: $counter"
运行结果:
=== 基础数学运算 ===
15 + 4 = 19
15 - 4 = 11
15 * 4 = 60
15 / 4 = 3 (整数除法)
15 % 4 = 3
15 ^ 2 = 225
=== 浮点数运算 ===
15 / 4 = 3.75 (浮点除法)
√15 ≈ 3.87
=== 递增递减 ===
初始值: 5
递增后: 6
递减后: 5
Day 4: 条件判断
bash
#!/bin/bash
# 条件判断示例
echo "=== 数值比较 ==="
read -p "请输入第一个数字: " a
read -p "请输入第二个数字: " b
if [ $a -eq $b ]; then
echo "$a 等于 $b"
fi
if [ $a -ne $b ]; then
echo "$a 不等于 $b"
fi
if [ $a -gt $b ]; then
echo "$a 大于 $b"
elif [ $a -lt $b ]; then
echo "$a 小于 $b"
else
echo "$a 等于 $b"
fi
echo -e "\n=== 字符串比较 ==="
read -p "请输入第一个字符串: " str1
read -p "请输入第二个字符串: " str2
if [ "$str1" = "$str2" ]; then
echo "字符串相同"
else
echo "字符串不同"
fi
if [ -z "$str1" ]; then
echo "第一个字符串为空"
fi
if [ -n "$str2" ]; then
echo "第二个字符串非空"
fi
echo -e "\n=== 文件检查 ==="
filename="test.txt"
echo "创建测试文件..."
echo "测试内容" > $filename
if [ -e $filename ]; then
echo "文件存在"
if [ -f $filename ]; then
echo "是普通文件"
fi
if [ -r $filename ]; then
echo "文件可读"
fi
if [ -w $filename ]; then
echo "文件可写"
fi
if [ -s $filename ]; then
echo "文件大小: $(wc -c < $filename) 字节"
fi
fi
# 清理
rm $filename
运行示例:
=== 数值比较 ===
请输入第一个数字: 10
请输入第二个数字: 5
10 不等于 5
10 大于 5
=== 字符串比较 ===
请输入第一个字符串: hello
请输入第二个字符串: world
字符串不同
第二个字符串非空
=== 文件检查 ===
创建测试文件...
文件存在
是普通文件
文件可读
文件可写
文件大小: 13 字节
Day 5: 逻辑运算符
bash
#!/bin/bash
# 逻辑运算符示例
echo "=== 逻辑运算符使用 ==="
read -p "请输入年龄: " age
read -p "是否有会员卡?(yes/no): " member
# AND 运算符
if [ $age -ge 18 ] && [ "$member" = "yes" ]; then
echo "可以享受会员折扣"
else
echo "不符合会员折扣条件"
fi
# OR 运算符
if [ $age -lt 12 ] || [ $age -ge 65 ]; then
echo "可以享受特殊优惠"
else
echo "不符合特殊优惠条件"
fi
# NOT 运算符
if ! [ "$member" = "no" ]; then
echo "你有会员身份"
else
echo "你不是会员"
fi
echo -e "\n=== 复合条件 ==="
read -p "请输入分数(0-100): " score
if [ $score -ge 90 ]; then
grade="A"
elif [ $score -ge 80 ] && [ $score -lt 90 ]; then
grade="B"
elif [ $score -ge 70 ] && [ $score -lt 80 ]; then
grade="C"
elif [ $score -ge 60 ] && [ $score -lt 70 ]; then
grade="D"
else
grade="F"
fi
echo "你的成绩等级是: $grade"
echo -e "\n=== 简化写法 ==="
# 使用双括号简化
if (( age >= 18 && age <= 30 )); then
echo "你是青年人"
fi
# 使用双方括号进行模式匹配
read -p "请输入文件名: " filename
if [[ $filename == *.txt ]]; then
echo "这是一个文本文件"
elif [[ $filename == *.jpg || $filename == *.png ]]; then
echo "这是一个图片文件"
else
echo "文件类型未知"
fi
运行示例:
=== 逻辑运算符使用 ===
请输入年龄: 25
是否有会员卡?(yes/no): yes
可以享受会员折扣
不符合特殊优惠条件
你有会员身份
=== 复合条件 ===
请输入分数(0-100): 85
你的成绩等级是: B
=== 简化写法 ===
你是青年人
请输入文件名: document.txt
这是一个文本文件
Day 6: Case语句
bash
#!/bin/bash
# Case语句示例
echo "=== 基础Case语句 ==="
read -p "请输入一个数字(1-7): " day
case $day in
1)
echo "星期一"
;;
2)
echo "星期二"
;;
3)
echo "星期三"
;;
4)
echo "星期四"
;;
5)
echo "星期五"
;;
6|7)
echo "周末"
;;
*)
echo "输入无效"
;;
esac
echo -e "\n=== 文件类型判断 ==="
read -p "请输入文件名: " filename
case $filename in
*.txt)
echo "文本文件"
;;
*.sh)
echo "Shell脚本"
;;
*.jpg|*.jpeg|*.png|*.gif)
echo "图片文件"
;;
*.tar|*.tar.gz|*.zip)
echo "压缩文件"
;;
*)
echo "未知文件类型"
;;
esac
echo -e "\n=== 系统命令菜单 ==="
echo "请选择操作:"
echo "1. 显示系统信息"
echo "2. 显示磁盘使用"
echo "3. 显示内存使用"
echo "4. 显示网络连接"
echo "5. 退出"
read -p "请输入选项(1-5): " option
case $option in
1)
echo "=== 系统信息 ==="
uname -a
;;
2)
echo "=== 磁盘使用 ==="
df -h
;;
3)
echo "=== 内存使用 ==="
free -h
;;
4)
echo "=== 网络连接 ==="
netstat -tuln | head -20
;;
5)
echo "退出程序"
exit 0
;;
*)
echo "无效选项"
;;
esac
运行示例:
=== 基础Case语句 ===
请输入一个数字(1-7): 3
星期三
=== 文件类型判断 ===
请输入文件名: script.sh
Shell脚本
=== 系统命令菜单 ===
请选择操作:
1. 显示系统信息
2. 显示磁盘使用
3. 显示内存使用
4. 显示网络连接
5. 退出
请输入选项(1-5): 1
=== 系统信息 ===
Linux server 5.4.0-100-generic #113-Ubuntu SMP Thu Feb 3 18:43:29 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
Day 7: For循环
bash
#!/bin/bash
# For循环示例
echo "=== 基础For循环 ==="
echo "数字列表:"
for i in 1 2 3 4 5; do
echo "数字: $i"
done
echo -e "\n=== 范围循环 ==="
echo "1到5:"
for i in {1..5}; do
echo "迭代: $i"
done
echo -e "\n带步长的循环:"
for i in {1..10..2}; do
echo "奇数: $i"
done
echo -e "\n=== C风格For循环 ==="
for ((i=1; i<=5; i++)); do
echo "计数: $i"
done
echo -e "\n=== 遍历数组 ==="
fruits=("苹果" "香蕉" "橙子" "葡萄" "西瓜")
echo "水果列表:"
for fruit in "${fruits[@]}"; do
echo " - $fruit"
done
echo -e "\n=== 遍历文件 ==="
echo "当前目录文件:"
for file in *; do
if [ -f "$file" ]; then
echo " 文件: $file"
elif [ -d "$file" ]; then
echo " 目录: $file"
fi
done
echo -e "\n=== 嵌套循环 ==="
echo "乘法表 (1-3):"
for i in {1..3}; do
for j in {1..3}; do
result=$((i * j))
echo -n "$i×$j=$result "
done
echo # 换行
done
echo -e "\n=== 带条件的循环 ==="
echo "小于5的偶数:"
for i in {1..10}; do
if (( i % 2 == 0 && i < 5 )); then
echo "找到偶数: $i"
fi
done
运行结果:
=== 基础For循环 ===
数字列表:
数字: 1
数字: 2
数字: 3
数字: 4
数字: 5
=== 范围循环 ===
1到5:
迭代: 1
迭代: 2
迭代: 3
迭代: 4
迭代: 5
带步长的循环:
奇数: 1
奇数: 3
奇数: 5
奇数: 7
奇数: 9
=== C风格For循环 ===
计数: 1
计数: 2
计数: 3
计数: 4
计数: 5
=== 遍历数组 ===
水果列表:
- 苹果
- 香蕉
- 橙子
- 葡萄
- 西瓜
=== 遍历文件 ===
当前目录文件:
文件: script.sh
目录: docs
=== 嵌套循环 ===
乘法表 (1-3):
1×1=1 1×2=2 1×3=3
2×1=2 2×2=4 2×3=6
3×1=3 3×2=6 3×3=9
=== 带条件的循环 ===
小于5的偶数:
找到偶数: 2
找到偶数: 4
Day 8: While循环
bash
#!/bin/bash
# While循环示例
echo "=== 基础While循环 ==="
counter=1
echo "从1数到5:"
while [ $counter -le 5 ]; do
echo "计数: $counter"
((counter++))
done
echo -e "\n=== 读取文件内容 ==="
# 创建测试文件
cat > students.txt << EOF
张三
李四
王五
赵六
EOF
echo "学生名单:"
while read student; do
echo " - $student"
done < students.txt
echo -e "\n=== 无限循环控制 ==="
echo "猜数字游戏 (1-10)"
secret=$((RANDOM % 10 + 1))
attempts=0
while true; do
read -p "请输入你的猜测: " guess
((attempts++))
if [ $guess -eq $secret ]; then
echo "恭喜!猜对了!"
echo "你用了 $attempts 次尝试"
break
elif [ $guess -lt $secret ]; then
echo "猜小了,再试试"
else
echo "猜大了,再试试"
fi
if [ $attempts -ge 5 ]; then
echo "机会用完了!正确答案是: $secret"
break
fi
done
echo -e "\n=== 条件循环 ==="
total=0
count=0
echo "输入数字求平均 (输入0结束):"
while read -p "请输入数字: " num && [ $num -ne 0 ]; do
total=$((total + num))
count=$((count + 1))
echo "当前总和: $total, 数字个数: $count"
done
if [ $count -gt 0 ]; then
average=$((total / count))
echo "平均值为: $average"
else
echo "没有输入数字"
fi
# 清理
rm students.txt
运行示例:
=== 基础While循环 ===
从1数到5:
计数: 1
计数: 2
计数: 3
计数: 4
计数: 5
=== 读取文件内容 ===
学生名单:
- 张三
- 李四
- 王五
- 赵六
=== 无限循环控制 ===
猜数字游戏 (1-10)
请输入你的猜测: 5
猜小了,再试试
请输入你的猜测: 8
猜大了,再试试
请输入你的猜测: 7
猜大了,再试试
请输入你的猜测: 6
恭喜!猜对了!
你用了 4 次尝试
=== 条件循环 ===
输入数字求平均 (输入0结束):
请输入数字: 10
当前总和: 10, 数字个数: 1
请输入数字: 20
当前总和: 30, 数字个数: 2
请输入数字: 30
当前总和: 60, 数字个数: 3
请输入数字: 0
平均值为: 20
Day 9: 数组操作
bash
#!/bin/bash
# 数组操作示例
echo "=== 数组基本操作 ==="
# 定义数组
colors=("红色" "绿色" "蓝色" "黄色" "紫色")
echo "颜色数组: ${colors[@]}"
echo "数组长度: ${#colors[@]}"
echo "第一个颜色: ${colors[0]}"
echo "最后一个颜色: ${colors[-1]}"
echo -e "\n=== 遍历数组 ==="
echo "所有颜色:"
for color in "${colors[@]}"; do
echo " - $color"
done
echo -e "\n带索引的遍历:"
for i in "${!colors[@]}"; do
echo " 索引 $i: ${colors[$i]}"
done
echo -e "\n=== 数组操作 ==="
# 添加元素
colors+=("橙色")
echo "添加橙色后: ${colors[@]}"
# 修改元素
colors[1]="深绿色"
echo "修改第二个元素后: ${colors[@]}"
# 删除元素
unset colors[2]
echo "删除第三个元素后: ${colors[@]}"
# 重新索引
colors=("${colors[@]}")
echo "重新索引后长度: ${#colors[@]}"
echo -e "\n=== 关联数组 ==="
declare -A student_scores
student_scores["张三"]=90
student_scores["李四"]=85
student_scores["王五"]=95
echo "学生成绩:"
for name in "${!student_scores[@]}"; do
echo " $name: ${student_scores[$name]}分"
done
echo -e "\n=== 数组排序 ==="
numbers=(45 12 89 3 67 23)
echo "原始数组: ${numbers[@]}"
# 冒泡排序
sorted=($(for num in "${numbers[@]}"; do echo $num; done | sort -n))
echo "排序后: ${sorted[@]}"
echo -e "\n=== 多维数组模拟 ==="
# 使用关联数组模拟二维数组
declare -A matrix
matrix[0,0]=1
matrix[0,1]=2
matrix[1,0]=3
matrix[1,1]=4
echo "矩阵:"
echo " ${matrix[0,0]} ${matrix[0,1]}"
echo " ${matrix[1,0]} ${matrix[1,1]}"
echo -e "\n=== 数组切片 ==="
original=("a" "b" "c" "d" "e" "f")
slice=("${original[@]:1:3}") # 从索引1开始取3个元素
echo "原始数组: ${original[@]}"
echo "切片(1-3): ${slice[@]}"
运行结果:
=== 数组基本操作 ===
颜色数组: 红色 绿色 蓝色 黄色 紫色
数组长度: 5
第一个颜色: 红色
最后一个颜色: 紫色
=== 遍历数组 ===
所有颜色:
- 红色
- 绿色
- 蓝色
- 黄色
- 紫色
带索引的遍历:
索引 0: 红色
索引 1: 绿色
索引 2: 蓝色
索引 3: 黄色
索引 4: 紫色
=== 数组操作 ===
添加橙色后: 红色 绿色 蓝色 黄色 紫色 橙色
修改第二个元素后: 红色 深绿色 蓝色 黄色 紫色 橙色
删除第三个元素后: 红色 深绿色 黄色 紫色 橙色
重新索引后长度: 5
=== 关联数组 ===
学生成绩:
李四: 85分
王五: 95分
张三: 90分
=== 数组排序 ===
原始数组: 45 12 89 3 67 23
排序后: 3 12 23 45 67 89
=== 多维数组模拟 ===
矩阵:
1 2
3 4
=== 数组切片 ===
原始数组: a b c d e f
切片(1-3): b c d
Day 10: 函数使用
bash
#!/bin/bash
# 函数示例
echo "=== 基础函数 ==="
# 定义函数
greet() {
local name=$1
echo "你好,$name!"
echo "欢迎使用Shell脚本!"
}
# 调用函数
greet "张三"
greet "李四"
echo -e "\n=== 函数参数 ==="
calculate_sum() {
local sum=0
for num in "$@"; do
sum=$((sum + num))
done
echo $sum
}
result=$(calculate_sum 1 2 3 4 5)
echo "1+2+3+4+5 = $result"
echo -e "\n=== 函数返回值 ==="
is_even() {
local num=$1
if [ $((num % 2)) -eq 0 ]; then
return 0 # 真
else
return 1 # 假
fi
}
check_number() {
read -p "请输入一个数字: " num
if is_even $num; then
echo "$num 是偶数"
else
echo "$num 是奇数"
fi
}
check_number
echo -e "\n=== 递归函数 ==="
factorial() {
local n=$1
if [ $n -le 1 ]; then
echo 1
else
local prev=$(factorial $((n - 1)))
echo $((n * prev))
fi
}
echo "5的阶乘: $(factorial 5)"
echo -e "\n=== 函数库示例 ==="
# 定义数学函数库
math_lib() {
case $1 in
add)
echo $(($2 + $3))
;;
subtract)
echo $(($2 - $3))
;;
multiply)
echo $(($2 * $3))
;;
divide)
if [ $3 -ne 0 ]; then
echo $(($2 / $3))
else
echo "错误:除数不能为0"
fi
;;
*)
echo "未知操作"
;;
esac
}
echo "10 + 5 = $(math_lib add 10 5)"
echo "10 - 5 = $(math_lib subtract 10 5)"
echo "10 * 5 = $(math_lib multiply 10 5)"
echo "10 / 5 = $(math_lib divide 10 5)"
echo "10 / 0 = $(math_lib divide 10 0)"
echo -e "\n=== 局部变量 ==="
test_scope() {
local local_var="我是局部变量"
global_var="我是全局变量"
echo "函数内 - 局部变量: $local_var"
echo "函数内 - 全局变量: $global_var"
}
test_scope
echo "函数外 - 局部变量: $local_var" # 输出空
echo "函数外 - 全局变量: $global_var"
运行示例:
=== 基础函数 ===
你好,张三!
欢迎使用Shell脚本!
你好,李四!
欢迎使用Shell脚本!
=== 函数参数 ===
1+2+3+4+5 = 15
=== 函数返回值 ===
请输入一个数字: 7
7 是奇数
=== 递归函数 ===
5的阶乘: 120
=== 函数库示例 ===
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
10 / 5 = 2
10 / 0 = 错误:除数不能为0
=== 局部变量 ===
函数内 - 局部变量: 我是局部变量
函数内 - 全局变量: 我是全局变量
函数外 - 局部变量:
函数外 - 全局变量: 我是全局变量
Day 11: 文件操作
bash
#!/bin/bash
# 文件操作示例
echo "=== 文件创建和写入 ==="
# 创建并写入文件
echo "这是第一行" > test.txt
echo "这是第二行" >> test.txt
echo "这是第三行" >> test.txt
echo "文件内容:"
cat test.txt
echo -e "\n=== 文件读取 ==="
echo "逐行读取:"
line_num=1
while IFS= read -r line; do
echo "行 $line_num: $line"
((line_num++))
done < test.txt
echo -e "\n=== 文件信息 ==="
echo "文件大小: $(wc -c < test.txt) 字节"
echo "行数: $(wc -l < test.txt)"
echo "单词数: $(wc -w < test.txt)"
echo -e "\n=== 文件查找和替换 ==="
# 查找包含"第二"的行
echo "包含'第二'的行:"
grep "第二" test.txt
# 替换文件内容
sed 's/第二/贰/' test.txt > test_modified.txt
echo "替换后的内容:"
cat test_modified.txt
echo -e "\n=== 文件比较 ==="
echo "比较两个文件差异:"
diff test.txt test_modified.txt
echo -e "\n=== 目录操作 ==="
mkdir -p test_dir/sub_dir
echo "创建目录结构完成"
echo "目录内容:"
ls -la test_dir/
echo -e "\n=== 文件权限 ==="
chmod 755 test.txt
echo "文件权限:"
ls -l test.txt
echo -e "\n=== 文件移动和重命名 ==="
mv test.txt renamed.txt
echo "文件重命名完成:"
ls -l *.txt
echo -e "\n=== 文件删除 ==="
rm renamed.txt test_modified.txt
rm -rf test_dir
echo "清理完成"
运行结果:
=== 文件创建和写入 ===
文件内容:
这是第一行
这是第二行
这是第三行
=== 文件读取 ===
逐行读取:
行 1: 这是第一行
行 2: 这是第二行
行 3: 这是第三行
=== 文件信息 ===
文件大小: 39 字节
行数: 3
单词数: 9
=== 文件查找和替换 ===
包含'第二'的行:
这是第二行
替换后的内容:
这是第一行
这是贰行
这是第三行
=== 文件比较 ===
比较两个文件差异:
2c2
< 这是第二行
---
> 这是贰行
=== 目录操作 ===
创建目录结构完成
目录内容:
总用量 8
drwxr-xr-x 3 user user 4096 Dec 25 10:30 .
drwxr-xr-x 4 user user 4096 Dec 25 10:30 ..
drwxr-xr-x 2 user user 4096 Dec 25 10:30 sub_dir
=== 文件权限 ===
文件权限:
-rwxr-xr-x 1 user user 39 Dec 25 10:30 test.txt
=== 文件移动和重命名 ===
文件重命名完成:
-rwxr-xr-x 1 user user 39 Dec 25 10:30 renamed.txt
-rw-r--r-- 1 user user 39 Dec 25 10:30 test_modified.txt
=== 文件删除 ===
清理完成
Day 12: 文本处理
bash
#!/bin/bash
# 文本处理示例
echo "=== 创建测试数据 ==="
cat > data.txt << EOF
张三,25,工程师,北京
李四,30,设计师,上海
王五,28,产品经理,深圳
赵六,35,经理,广州
钱七,22,实习生,杭州
EOF
echo "原始数据:"
cat data.txt
echo -e "\n=== 文本过滤 ==="
echo "年龄大于25岁的员工:"
awk -F, '$2 > 25' data.txt
echo -e "\n=== 文本排序 ==="
echo "按年龄排序:"
sort -t, -k2,2n data.txt
echo -e "\n按姓名排序:"
sort -t, -k1,1 data.txt
echo -e "\n=== 文本统计 ==="
echo "各职位人数统计:"
awk -F, '{count[$3]++} END {for(job in count) print job ": " count[job] "人"}' data.txt
echo -e "\n=== 文本转换 ==="
echo "转换为HTML表格:"
echo "<table border='1'>"
echo "<tr><th>姓名</th><th>年龄</th><th>职位</th><th>城市</th></tr>"
awk -F, '{print "<tr><td>" $1 "</td><td>" $2 "</td><td>" $3 "</td><td>" $4 "</td></tr>"}' data.txt
echo "</table>"
echo -e "\n=== 字段提取 ==="
echo "提取姓名和职位:"
cut -d, -f1,3 data.txt
echo -e "\n=== 文本搜索 ==="
echo "查找'经理':"
grep "经理" data.txt
echo -e "\n=== 文本替换 ==="
echo "将'经理'替换为'主管':"
sed 's/经理/主管/g' data.txt
echo -e "\n=== 去重处理 ==="
echo "添加重复数据..."
echo "张三,25,工程师,北京" >> data.txt
echo "去重后:"
sort data.txt | uniq
echo -e "\n=== 行号处理 ==="
echo "显示行号:"
nl data.txt
echo -e "\n=== 合并文件 ==="
cat > extra_data.txt << EOF
孙八,40,总监,南京
周九,26,开发,成都
EOF
echo "合并后的数据:"
cat data.txt extra_data.txt
# 清理
rm data.txt extra_data.txt
运行结果:
=== 创建测试数据 ===
原始数据:
张三,25,工程师,北京
李四,30,设计师,上海
王五,28,产品经理,深圳
赵六,35,经理,广州
钱七,22,实习生,杭州
=== 文本过滤 ===
年龄大于25岁的员工:
李四,30,设计师,上海
王五,28,产品经理,深圳
赵六,35,经理,广州
=== 文本排序 ===
按年龄排序:
钱七,22,实习生,杭州
张三,25,工程师,北京
王五,28,产品经理,深圳
李四,30,设计师,上海
赵六,35,经理,广州
按姓名排序:
李四,30,设计师,上海
王五,28,产品经理,深圳
钱七,22,实习生,杭州
张三,25,工程师,北京
赵六,35,经理,广州
=== 文本统计 ===
各职位人数统计:
设计师: 1人
产品经理: 1人
经理: 1人
工程师: 1人
实习生: 1人
=== 文本转换 ===
转换为HTML表格:
<table border='1'>
<tr><th>姓名</th><th>年龄</th><th>职位</th><th>城市</th></tr>
<tr><td>张三</td><td>25</td><td>工程师</td><td>北京</td></tr>
<tr><td>李四</td><td>30</td><td>设计师</td><td>上海</td></tr>
<tr><td>王五</td><td>28</td><td>产品经理</td><td>深圳</td></tr>
<tr><td>赵六</td><td>35</td><td>经理</td><td>广州</td></tr>
<tr><td>钱七</td><td>22</td><td>实习生</td><td>杭州</td></tr>
</table>
=== 字段提取 ===
提取姓名和职位:
张三,工程师
李四,设计师
王五,产品经理
赵六,经理
钱七,实习生
=== 文本搜索 ===
查找'经理':
王五,28,产品经理,深圳
赵六,35,经理,广州
=== 文本替换 ===
将'经理'替换为'主管':
张三,25,工程师,北京
李四,30,设计师,上海
王五,28,产品主管,深圳
赵六,35,主管,广州
钱七,22,实习生,杭州
=== 去重处理 ===
添加重复数据...
去重后:
张三,25,工程师,北京
李四,30,设计师,上海
王五,28,产品经理,深圳
赵六,35,经理,广州
钱七,22,实习生,杭州
=== 行号处理 ===
显示行号:
1 张三,25,工程师,北京
2 李四,30,设计师,上海
3 王五,28,产品经理,深圳
4 赵六,35,经理,广州
5 钱七,22,实习生,杭州
6 张三,25,工程师,北京
=== 合并文件 ===
合并后的数据:
张三,25,工程师,北京
李四,30,设计师,上海
王五,28,产品经理,深圳
赵六,35,经理,广州
钱七,22,实习生,杭州
张三,25,工程师,北京
孙八,40,总监,南京
周九,26,开发,成都
Day 13: 正则表达式
bash
#!/bin/bash
# 正则表达式示例
echo "=== 创建测试数据 ==="
cat > text.txt << EOF
我的电话是: 138-1234-5678
电子邮件: john.doe@example.com
另一个邮箱: alice_smith123@gmail.com
URL: https://www.example.com/page
IP地址: 192.168.1.1
日期: 2024-12-25
时间: 14:30:45
身份证: 110101199001011234
邮政编码: 100000
EOF
echo "测试文本内容:"
cat text.txt
echo -e "\n=== 基础匹配 ==="
echo "查找所有邮箱地址:"
grep -E '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' text.txt
echo -e "\n查找所有电话号码:"
grep -E '[0-9]{3}-[0-9]{4}-[0-9]{4}' text.txt
echo -e "\n=== 复杂匹配 ==="
echo "查找URL:"
grep -E 'https?://[a-zA-Z0-9./?=_%:-]+' text.txt
echo -e "\n查找IP地址:"
grep -E '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' text.txt
echo -e "\n查找日期:"
grep -E '[0-9]{4}-[0-9]{2}-[0-9]{2}' text.txt
echo -e "\n=== 使用sed进行替换 ==="
echo "隐藏电话号码中间四位:"
sed -E 's/([0-9]{3}-)[0-9]{4}(-[0-9]{4})/\1****\2/' text.txt
echo -e "\n隐藏邮箱用户名:"
sed -E 's/([a-zA-Z0-9._%+-]+)@/@/' text.txt | sed 's/@/@***/' | sed 's/@***/@***./'
echo -e "\n=== 使用awk提取 ==="
echo "提取时间部分:"
awk 'match($0, /([0-9]{2}:[0-9]{2}:[0-9]{2})/, arr) {print arr[1]}' text.txt
echo -e "\n=== 分组捕获 ==="
echo "提取日期各部分:"
grep -E '([0-9]{4})-([0-9]{2})-([0-9]{2})' text.txt | while read line; do
if [[ $line =~ ([0-9]{4})-([0-9]{2})-([0-9]{2}) ]]; then
echo "年: ${BASH_REMATCH[1]}, 月: ${BASH_REMATCH[2]}, 日: ${BASH_REMATCH[3]}"
fi
done
echo -e "\n=== 验证格式 ==="
echo "验证身份证格式 (18位):"
grep -E '^[1-9][0-9]{5}(18|19|20)[0-9]{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)[0-9]{3}[0-9Xx]$' text.txt
echo -e "\n验证邮政编码 (6位数字):"
grep -E '^[1-9][0-9]{5}$' text.txt
echo -e "\n=== 负向匹配 ==="
echo "查找不含数字的行:"
grep -v '[0-9]' text.txt
echo -e "\n=== 边界匹配 ==="
echo "查找以'我的'开头的行:"
grep '^我的' text.txt
echo "查找以'com'结尾的行:"
grep 'com$' text.txt
# 清理
rm text.txt
运行结果:
=== 创建测试数据 ===
测试文本内容:
我的电话是: 138-1234-5678
电子邮件: john.doe@example.com
另一个邮箱: alice_smith123@gmail.com
URL: https://www.example.com/page
IP地址: 192.168.1.1
日期: 2024-12-25
时间: 14:30:45
身份证: 110101199001011234
邮政编码: 100000
=== 基础匹配 ===
查找所有邮箱地址:
电子邮件: john.doe@example.com
另一个邮箱: alice_smith123@gmail.com
查找所有电话号码:
我的电话是: 138-1234-5678
=== 复杂匹配 ===
查找URL:
URL: https://www.example.com/page
查找IP地址:
IP地址: 192.168.1.1
查找日期:
日期: 2024-12-25
=== 使用sed进行替换 ===
隐藏电话号码中间四位:
我的电话是: 138-****-5678
电子邮件: john.doe@example.com
另一个邮箱: alice_smith123@gmail.com
URL: https://www.example.com/page
IP地址: 192.168.1.1
日期: 2024-12-25
时间: 14:30:45
身份证: 110101199001011234
邮政编码: 100000
隐藏邮箱用户名:
我的电话是: 138-1234-5678
电子邮件: @***.example.com
另一个邮箱: @***.gmail.com
URL: https://www.example.com/page
IP地址: 192.168.1.1
日期: 2024-12-25
时间: 14:30:45
身份证: 110101199001011234
邮政编码: 100000
=== 使用awk提取 ===
提取时间部分:
14:30:45
=== 分组捕获 ===
提取日期各部分:
年: 2024, 月: 12, 日: 25
=== 验证格式 ===
验证身份证格式 (18位):
身份证: 110101199001011234
验证邮政编码 (6位数字):
邮政编码: 100000
=== 负向匹配 ===
查找不含数字的行:
(没有输出)
=== 边界匹配 ===
查找以'我的'开头的行:
我的电话是: 138-1234-5678
查找以'com'结尾的行:
电子邮件: john.doe@example.com
另一个邮箱: alice_smith123@gmail.com
Day 14: 进程管理
bash
#!/bin/bash
# 进程管理示例
echo "=== 进程信息 ==="
echo "当前Shell的PID: $$"
echo "父进程PID: $PPID"
echo "当前用户: $(whoami)"
echo -e "\n=== 运行进程 ==="
echo "在后台运行sleep命令:"
sleep 10 &
sleep_pid=$!
echo "sleep进程PID: $sleep_pid"
echo -e "\n运行多个后台进程:"
for i in {1..3}; do
sleep 5 &
echo "启动进程 $i, PID: $!"
done
echo -e "\n=== 进程列表 ==="
echo "当前用户进程:"
ps -u $(whoami) | head -10
echo -e "\n进程树:"
pstree -p $(whoami) | head -15
echo -e "\n=== 进程监控 ==="
echo "CPU使用率最高的进程:"
ps aux --sort=-%cpu | head -5
echo -e "\n内存使用率最高的进程:"
ps aux --sort=-%mem | head -5
echo -e "\n=== 进程控制 ==="
echo "等待sleep进程完成:"
wait $sleep_pid
echo "sleep进程已完成"
echo -e "\n启动一个长时间运行的进程:"
echo "运行ping命令 (5秒后停止)..."
ping -c 10 google.com > /dev/null &
ping_pid=$!
sleep 5
echo "停止ping进程 (PID: $ping_pid)"
kill $ping_pid
if wait $ping_pid 2>/dev/null; then
echo "进程正常结束"
else
echo "进程被终止"
fi
echo -e "\n=== 作业控制 ==="
echo "启动两个作业:"
sleep 20 &
job1=$!
echo "作业1 (sleep 20): PID=$job1"
sleep 30 &
job2=$!
echo "作业2 (sleep 30): PID=$job2"
echo -e "\n作业列表:"
jobs
echo -e "\n将作业2放到前台:"
fg %2 &
sleep 2
echo -e "\n将作业2放回后台:"
bg %2
echo -e "\n终止所有sleep进程:"
pkill sleep
echo "已终止所有sleep进程"
echo -e "\n=== 进程间通信 ==="
echo "创建命名管道:"
mkfifo my_pipe
echo "通过管道发送数据..."
echo "Hello from process 1" > my_pipe &
cat my_pipe &
wait
rm my_pipe
echo -e "\n=== 信号处理 ==="
trap "echo '收到SIGINT信号'; exit 1" SIGINT
trap "echo '收到SIGTERM信号'; exit 1" SIGTERM
echo "按Ctrl+C发送SIGINT信号"
echo "等待5秒..."
sleep 5
echo "没有收到信号,继续执行"
# 重置信号处理
trap - SIGINT SIGTERM
echo -e "\n=== 系统资源限制 ==="
echo "打开文件数限制:"
ulimit -n
echo "进程数限制:"
ulimit -u
运行结果:
=== 进程信息 ===
当前Shell的PID: 12345
父进程PID: 12340
当前用户: user
=== 运行进程 ===
在后台运行sleep命令:
sleep进程PID: 12346
运行多个后台进程:
启动进程 1, PID: 12347
启动进程 2, PID: 12348
启动进程 3, PID: 12349
=== 进程列表 ===
当前用户进程:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
user 12345 0.0 0.0 12345 6789 pts/0 S 10:30 0:00 bash
user 12346 0.0 0.0 12346 1234 pts/0 S 10:30 0:00 sleep 10
进程树:
bash(12345)─┬─sleep(12346)
├─sleep(12347)
├─sleep(12348)
└─sleep(12349)
=== 进程监控 ===
CPU使用率最高的进程:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
user 12350 5.0 0.0 12350 1234 pts/0 R 10:30 0:00 ps aux --sort=-%cpu
内存使用率最高的进程:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
user 12351 0.0 0.1 123456 12345 pts/0 S 10:30 0:00 some_process
=== 进程控制 ===
等待sleep进程完成:
sleep进程已完成
启动一个长时间运行的进程:
运行ping命令 (5秒后停止)...
停止ping进程 (PID: 12352)
进程被终止
=== 作业控制 ===
启动两个作业:
作业1 (sleep 20): PID=12353
作业2 (sleep 30): PID=12354
作业列表:
[1]- 运行中 sleep 20 &
[2]+ 运行中 sleep 30 &
将作业2放到前台:
[2]+ 继续 sleep 30
将作业2放回后台:
[2]+ sleep 30 &
终止所有sleep进程:
已终止所有sleep进程
=== 进程间通信 ===
创建命名管道:
通过管道发送数据...
Hello from process 1
=== 信号处理 ===
按Ctrl+C发送SIGINT信号
等待5秒...
^C收到SIGINT信号
=== 系统资源限制 ===
打开文件数限制: 1024
进程数限制: 12345
由于篇幅限制,这里只展示了前14天的示例。如果你需要完整的30天示例,我可以继续提供剩余的内容。每个示例都包含详细的注释和预期的运行结果,帮助你逐步掌握Shell脚本编程。