Shell 脚本

返回 Linux


基本结构

#!/bin/bash
# 第一行 shebang 指定解释器
 
set -euo pipefail   # e=遇错退出 u=未定义变量报错 o pipefail=管道错误传递
 
echo "Hello, $1"
chmod +x script.sh   # 添加执行权限
./script.sh arg1     # 执行
bash script.sh arg1  # 直接用 bash 执行(无需执行权限)

变量

name="Alice"
echo $name
echo "${name}!"      # 花括号消除歧义
 
readonly MAX=100     # 只读变量
 
# 命令替换
today=$(date +%Y-%m-%d)
 
# 算术
count=5
echo $((count + 3))
((count++))

特殊变量

变量说明
$0脚本名称
$1 ~ $9位置参数
$#参数个数
$@所有参数(各自独立)
$?上条命令退出码(0 成功)
$$当前进程 PID
$!最近后台进程 PID

条件判断

if [ "$1" == "start" ]; then
    echo "Starting..."
elif [ "$1" == "stop" ]; then
    echo "Stopping..."
else
    echo "Unknown command"
fi
 
# 文件测试
[ -f "/etc/nginx/nginx.conf" ] && echo "文件存在"
[ -d "/opt/app" ]              && echo "目录存在"
 
# 数值比较(-eq -ne -gt -ge -lt -le)
[ $count -gt 10 ] && echo "大于10"
 
# 字符串
[ -z "$var" ] && echo "空字符串"
[ -n "$var" ] && echo "非空字符串"
 
# 多条件([[ ]] 支持 && ||)
[[ $age -ge 18 && $age -lt 60 ]] && echo "工作年龄"

循环

# for 遍历列表
for file in /var/log/*.log; do
    echo "Processing $file"
done
 
# for 数字范围
for i in {1..5}; do echo $i; done
for ((i=0; i<10; i++)); do echo $i; done
 
# while
count=0
while [ $count -lt 5 ]; do
    echo $count
    ((count++))
done
 
# 逐行读取文件
while IFS= read -r line; do
    echo "$line"
done < /etc/hosts
 
# until(条件为假时执行)
until ping -c1 host &>/dev/null; do
    sleep 5
done

函数

log() {
    local level=$1
    local msg=$2
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $msg"
}
 
log INFO "服务启动"
log ERROR "连接失败"
 
# 返回值(整数 0-255)
is_running() {
    pgrep -x "$1" > /dev/null
    return $?
}
 
is_running nginx && echo "nginx 运行中"
 
# 返回字符串:用 echo + 命令替换
get_version() { echo "1.0.0"; }
ver=$(get_version)

错误处理

set -euo pipefail
 
# 退出时清理
trap 'rm -f /tmp/myapp.lock' EXIT
trap 'echo "中断"; exit 1' INT TERM
 
# 自定义 die 函数
die() {
    echo "ERROR: $1" >&2
    exit "${2:-1}"
}
 
[ -f "$config" ] || die "配置文件不存在: $config"

字符串操作

str="Hello, World!"
echo ${#str}              # 长度
echo ${str:7:5}           # 截取:World
echo ${str/World/Linux}   # 替换一次
echo ${str//l/L}          # 全局替换
echo ${str,,}             # 转小写
echo ${str^^}             # 转大写
 
# 前缀 / 后缀删除
file="archive.tar.gz"
echo ${file%.gz}          # archive.tar
echo ${file%%.*}          # archive
echo ${file##*.}          # gz

数组

arr=("apple" "banana" "cherry")
echo ${arr[0]}            # 第一个元素
echo ${arr[@]}            # 所有元素
echo ${#arr[@]}           # 元素个数
arr+=("date")             # 追加
 
for item in "${arr[@]}"; do echo "$item"; done
 
# 关联数组
declare -A map
map["name"]="Alice"
map["age"]=30
echo ${map["name"]}

实用片段

# 检查 root 权限
[ "$(id -u)" -ne 0 ] && echo "需要 root 权限" && exit 1
 
# 获取脚本所在目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
 
# 加锁防并发
LOCK="/tmp/$(basename $0).lock"
exec 9>"$LOCK"
flock -n 9 || { echo "脚本已在运行"; exit 1; }
 
# 带超时等待服务就绪
timeout 30 bash -c 'until curl -sf http://localhost:8080/health; do sleep 1; done'

相关文档