Shell脚本编程高频问题20问
Shell脚本是运维自动化的核心工具。本文整理了最常见的问题和解决方案。
Q1: 如何安全地写Shell脚本?
A: 开头加上这三行:
#!/bin/bash set -euo pipefail # -e: 任何命令失败立即退出 # -u: 使用未定义变量报错 # -o pipefail: 管道中任一命令失败都算失败
Q2: 如何传递和处理参数?
#!/bin/bash
# 脚本名: backup.sh
# 用法: ./backup.sh /source /dest
SOURCE="$1" # 第一个参数
DEST="$2" # 第二个参数
shift 2 # 移走前两个参数
EXTRA=("$@") # 剩余所有参数
# 参数检查
if [ $# -lt 2 ]; then
echo "用法: $0 <源目录> <目标目录>"
exit 1
fi
Q3: 如何读取用户输入?
# 简单读取
read -p "请输入用户名: " username
echo "你好, $username"
# 隐藏输入(密码)
read -s -p "请输入密码: " password
echo
# 带默认值
read -p "请输入端口 [默认8080]: " port
port=${port:-8080}
Q4: 如何进行条件判断?
# 字符串比较 if [ "$var" = "value" ]; then ... fi if [ -z "$var" ]; then echo "变量为空"; fi # -z 长度为0 if [ -n "$var" ]; then echo "变量非空"; fi # -n 长度非0 # 数字比较 if [ "$num" -eq 10 ]; then ... fi # 等于 if [ "$num" -gt 10 ]; then ... fi # 大于 if [ "$num" -lt 10 ]; then ... fi # 小于 # 文件判断 if [ -f "file.txt" ]; then ... fi # 文件存在 if [ -d "directory" ]; then ... fi # 目录存在 if [ -r "file.txt" ]; then ... fi # 可读 if [ -w "file.txt" ]; then ... fi # 可写 if [ -x "script.sh" ]; then ... fi # 可执行
Q5: 如何写循环?
# for循环(列表)
for i in 1 2 3 4 5; do
echo $i
done
# for循环(范围)
for i in {1..10}; do
echo $i
done
# for循环(命令输出)
for file in $(ls *.txt); do
echo "处理: $file"
done
# while循环
while IFS= read -r line; do
echo "$line"
done < file.txt
# 无限循环
while true; do
echo "运行中..."
sleep 60
done
Q6: 如何定义函数?
# 定义函数
log() {
local level="$1" # local声明局部变量
shift
local message="$*"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message"
}
# 调用函数
log INFO "开始备份"
log ERROR "备份失败"
# 带返回值的函数
get_cpu_usage() {
local usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}')
echo "$usage" # 用echo返回
}
cpu=$(get_cpu_usage)
echo "CPU使用率: $cpu%"
Q7: 如何处理错误?
# 检查命令返回值
if ! mkdir -p /backup/data; then
echo "创建目录失败"
exit 1
fi
# 或者简写
mkdir -p /backup/data || { echo "创建目录失败"; exit 1; }
# 使用trap捕获错误
trap 'echo "脚本在第 $LINENO 行出错"' ERR
# 清理函数
cleanup() {
echo "清理临时文件..."
rm -f /tmp/temp_*.txt
}
trap cleanup EXIT # 脚本退出时执行清理
Q8: 如何操作字符串?
str="Hello World"
# 长度
${#str} # 11
# 截取
${str:0:5} # Hello(从0开始,取5个字符)
${str:6} # World(从6开始到结尾)
${str: -5} # World(最后5个字符,注意空格)
# 替换
${str/World/Bash} # Hello Bash(第一个匹配)
${str//l/L} # HeLLo WorLd(全部匹配)
${str/#Hello/Hi} # Hi World(开头匹配)
${str/%World/Earth} # Hello Earth(结尾匹配)
# 删除匹配
${str#Hello } # World(删除最短匹配的前缀)
${str##*l} # d(删除最长匹配的前缀)
${str% World} # Hello(删除最短匹配的后缀)
${str%% *} # Hello(删除最长匹配的后缀)
更多问题速查
- Q9: 数组怎么用? → arr=(a b c); echo ${arr[0]}
- Q10: 如何并发执行? → command & 后台执行
- Q11: 如何等待所有后台任务? → wait
- Q12: 如何重定向输出? → >覆盖 >>追加 2>&1合并错误
- Q13: 如何读取配置文件? → source config.sh
- Q14: 如何生成随机数? → $RANDOM
- Q15: 如何获取脚本路径? → $(dirname "$0")
- Q16: 如何调试脚本? → bash -x script.sh
- Q17: 如何比较版本号? → sort -V
- Q18: 如何发送HTTP请求? → curl 或 wget
- Q19: 如何解析JSON? → jq工具
- Q20: 如何发送邮件? → mail 或 sendmail
总结
Shell脚本的核心是熟练运用变量、条件、循环、函数。记住set -euo pipefail,养成良好的脚本编写习惯。
