导读:厌倦了重复执行相同的命令序列?想要让你的Linux系统自动完成繁琐任务?Shell脚本就是你的不二选择!本文将带你从零开始掌握Shell脚本编程,从基本语法到高级技巧,让你的工作效率提升10倍。无论你是Linux新手还是经验丰富的管理员,这篇文章都能帮你解锁Shell脚本的强大潜力!
本文是《从入门到精通渐进式学习Linux》系列的第12章。通过这篇文章,你将学会如何用Shell脚本实现系统管理自动化。
Shell脚本是由Shell命令组成的文本文件,能够自动执行一系列的命令,就像一个小型程序一样。在Linux中,最常用的Shell是Bash (Bourne Again SHell)。
nano hello.sh
#!/bin/bash
# 我的第一个脚本
echo "Hello, Shell世界!"
echo "当前时间是: $(date)"
echo "当前用户: $USER"
保存并退出
添加执行权限:
chmod +x hello.sh
./hello.sh
#!/bin/bash
告诉系统使用哪个Shell解释器#
开头的行在Bash中定义和使用变量非常简单:
# 定义变量 (注意=两边不能有空格)
name="小明"
age=28
# 使用变量 (使用$符号)
echo "你好,$name!你今年$age岁了。"
# 命令结果赋值
current_directory=$(pwd)
files_count=$(ls | wc -l)
echo "当前目录: $current_directory"
echo "文件数量: $files_count"
系统中预定义的环境变量:
echo "用户名: $USER"
echo "主目录: $HOME"
echo "当前Shell: $SHELL"
echo "PATH: $PATH"
定义自己的环境变量:
export MY_VARIABLE="全局变量值"
Bash提供了一些特殊变量:
echo "脚本名称: $0"
echo "第一个参数: $1" # 脚本运行时传入的第一个参数
echo "参数个数: $#"
echo "所有参数: $@"
echo "上一条命令的退出状态: $?"
echo "当前进程ID: $$"
Bash支持简单的数组操作:
# 定义数组
fruits=("苹果" "香蕉" "橙子" "葡萄")
# 访问数组元素
echo "第一个水果: ${fruits[0]}"
echo "所有水果: ${fruits[@]}"
echo "水果数量: ${#fruits[@]}"
# 添加元素
fruits+=("西瓜")
# 遍历数组
for fruit in "${fruits[@]}"; do
echo "水果: $fruit"
done
基本语法:
if [ 条件 ]; then
# 如果条件为真,执行这里的代码
elif [ 另一个条件 ]; then
# 如果另一个条件为真,执行这里的代码
else
# 如果以上条件都不为真,执行这里的代码
fi
示例:
#!/bin/bash
age=25
if [ "$age" -lt 18 ]; then
echo "你是未成年"
elif [ "$age" -ge 18 ] && [ "$age" -lt 60 ]; then
echo "你是成年人"
else
echo "你是老年人"
fi
# 文件判断
if [ -f "/etc/passwd" ]; then
echo "/etc/passwd 文件存在"
fi
if [ -d "/etc" ]; then
echo "/etc 是一个目录"
fi
文件测试:
操作符 | 描述 |
---|---|
-f 文件 | 如果文件存在且是普通文件,则为真 |
-d 目录 | 如果目录存在,则为真 |
-r 文件 | 如果文件存在且可读,则为真 |
-w 文件 | 如果文件存在且可写,则为真 |
-x 文件 | 如果文件存在且可执行,则为真 |
-s 文件 | 如果文件存在且不为空,则为真 |
字符串比较:
操作符 | 描述 |
---|---|
-z 字符串 | 如果字符串长度为0,则为真 |
-n 字符串 | 如果字符串长度不为0,则为真 |
字符串1 = 字符串2 | 如果字符串相同,则为真 |
字符串1 != 字符串2 | 如果字符串不同,则为真 |
数值比较:
操作符 | 描述 |
---|---|
-eq | 相等 |
-ne | 不相等 |
-gt | 大于 |
-ge | 大于等于 |
-lt | 小于 |
-le | 小于等于 |
逻辑操作:
操作符 | 描述 |
---|---|
&& | 逻辑与 |
|| | 逻辑或 |
! | 逻辑非 |
case语句类似于其他语言中的switch-case:
#!/bin/bash
fruit="苹果"
case "$fruit" in
"苹果")
echo "这是一个苹果"
;;
"香蕉" | "芭蕉")
echo "这是一个香蕉或芭蕉"
;;
*)
echo "未知水果"
;;
esac
基本语法:
for 变量 in 列表
do
# 循环体
done
示例:
# 遍历数字
for i in 1 2 3 4 5
do
echo "计数: $i"
done
# 使用序列生成器
for i in {1..10..2} # 1到10,步长为2
do
echo "序列: $i"
done
# 遍历文件
for file in /etc/*.conf
do
echo "配置文件: $file"
done
# C风格for循环
for ((i=1; i<=5; i++))
do
echo "C风格循环: $i"
done
语法:
while [ 条件 ]
do
# 循环体
done
示例:
# 简单计数器
count=1
while [ $count -le 5 ]
do
echo "While循环计数: $count"
count=$((count + 1))
done
# 读取文件每一行
while read line
do
echo "读取行: $line"
done < /etc/hostname
until循环与while循环相反,直到条件为真时停止:
count=1
until [ $count -gt 5 ]
do
echo "Until循环计数: $count"
count=$((count + 1))
done
break
: 跳出循环continue
: 跳过当前迭代,继续下一次循环for i in {1..10}
do
if [ $i -eq 5 ]
then
continue # 跳过5
fi
if [ $i -eq 8 ]
then
break # 循环到8就结束
fi
echo "数字: $i"
done
# 方法1
function greeting {
echo "你好,$1!"
}
# 方法2
say_goodbye() {
echo "再见,$1!"
}
greeting "小明"
say_goodbye "小红"
function calculate {
local sum=$(($1 + $2)) # local关键字定义局部变量
echo "计算结果: $sum"
return 0 # 返回状态码
}
calculate 5 3
echo "函数返回值: $?"
Bash函数通过return
只能返回状态码(0-255),如果要返回计算结果:
# 方法1: 使用echo
get_sum() {
echo $(($1 + $2))
}
result=$(get_sum 10 20)
echo "和: $result"
# 方法2: 使用全局变量
calculate_product() {
product=$(($1 * $2))
}
calculate_product 4 5
echo "乘积: $product"
Shell脚本经常需要处理文本数据,以下是几个常用工具:
# 在文件中搜索关键字
grep "root" /etc/passwd
# 递归搜索目录
grep -r "配置" /etc/
# 显示匹配行号
grep -n "bash" /etc/passwd
# 只显示匹配的文件名
grep -l "sshd" /etc/pam.d/*
# 替换文本
echo "hello world" | sed 's/world/linux/'
# 删除空行
sed '/^$/d' file.txt
# 打印特定行
sed -n '5,10p' file.txt
# 多个操作
sed -e 's/foo/bar/g' -e 's/hello/hi/g' file.txt
# 打印特定列
echo "one two three" | awk '{print $2}'
# 使用不同的分隔符
awk -F: '{print $1, $6}' /etc/passwd
# 条件过滤
awk '$3 > 1000 {print $1}' /etc/passwd
# 求和
awk '{sum += $1} END {print sum}' numbers.txt
# 提取特定字段
cut -d: -f1,6 /etc/passwd
# 提取字符范围
echo "abcdefg" | cut -c1-3
# 普通排序
sort names.txt
# 数值排序
sort -n numbers.txt
# 反向排序
sort -r names.txt
# 按特定字段排序
sort -t: -k3 -n /etc/passwd
#!/bin/bash
# 简单备份脚本
BACKUP_DIR="/backup"
DATE=$(date +%Y%m%d)
BACKUP_FILE="$BACKUP_DIR/home_$DATE.tar.gz"
# 检查备份目录
if [ ! -d "$BACKUP_DIR" ]; then
mkdir -p "$BACKUP_DIR"
echo "创建备份目录 $BACKUP_DIR"
fi
# 执行备份
echo "开始备份..."
tar -czf "$BACKUP_FILE" /home
echo "备份完成: $BACKUP_FILE"
# 保留最近7天的备份
find "$BACKUP_DIR" -name "home_*.tar.gz" -mtime +7 -delete
echo "已删除7天前的备份"
#!/bin/bash
# 监控CPU、内存和磁盘使用情况
echo "=== 系统监控报告 - $(date) ==="
echo -e "\n>>> CPU负载 <<<"
uptime
echo -e "\n>>> 内存使用情况 <<<"
free -h
echo -e "\n>>> 磁盘使用情况 <<<"
df -h
echo -e "\n>>> 占用CPU最多的5个进程 <<<"
ps aux | sort -rk 3,3 | head -6
echo -e "\n>>> 占用内存最多的5个进程 <<<"
ps aux | sort -rk 4,4 | head -6
echo "=== 监控报告结束 ==="
#!/bin/bash
# 批量创建用户
# 用法: ./create_users.sh users.txt
if [ "$#" -ne 1 ]; then
echo "用法: $0 <用户列表文件>"
exit 1
fi
if [ ! -f "$1" ]; then
echo "错误: 文件 $1 不存在"
exit 2
fi
# 逐行读取用户信息并创建用户
while IFS=: read -r username password
do
echo "创建用户: $username"
# 检查用户是否已存在
if id "$username" &>/dev/null; then
echo "警告: 用户 $username 已存在,跳过"
continue
fi
# 创建用户
useradd -m -s /bin/bash "$username"
echo "$username:$password" | chpasswd
echo "用户 $username 创建完成"
done < "$1"
echo "所有用户创建完毕"
#!/bin/bash
# 监控网站可用性
SITES=("https://www.baidu.com" "https://www.google.com" "https://www.github.com")
LOG_FILE="/var/log/website_check.log"
ADMIN_EMAIL="[email protected]"
check_website() {
status_code=$(curl -s -o /dev/null -w "%{http_code}" "$1")
if [ "$status_code" -eq 200 ] || [ "$status_code" -eq 301 ]; then
return 0
else
return 1
fi
}
echo "$(date): 开始网站检查" >> "$LOG_FILE"
for site in "${SITES[@]}"; do
if check_website "$site"; then
echo "$(date): $site 正常运行" >> "$LOG_FILE"
else
echo "$(date): $site 无法访问!" >> "$LOG_FILE"
echo "网站 $site 不可访问,请检查!" | mail -s "网站故障警报" "$ADMIN_EMAIL"
fi
done
echo "$(date): 完成网站检查" >> "$LOG_FILE"
Shell脚本是Linux系统管理的强大工具,通过本文,你学习了:
掌握Shell脚本编程,你将能够自动化许多重复性工作,提高工作效率,并更加深入地理解Linux系统。随着经验的积累,你可以编写更加复杂和强大的脚本来解决实际问题。
如果你觉得这篇文章对你有帮助,请不要忘记点赞、收藏和关注!你的支持是我创作的最大动力!
❓ 有任何问题或建议,欢迎在评论区留言讨论!
本文是《从入门到精通渐进式学习Linux》系列的第12章。下一章我们将探讨Linux系统监控与性能优化,敬请期待!