1️⃣ 变量基础
变量定义与赋值
#!/bin/bash # 定义变量(=两侧不能有空格!) name="张运维" age=28 city="濮阳" # 引用变量:$变量名 或 ${变量名}(推荐后者) echo "姓名:$name" echo "年龄:${age} 岁" # 命令替换:$() 或 反引号(推荐前者) today=$(date '+%Y-%m-%d') hostname=$(hostname) echo "今天是 $today,主机为 $hostname" # 只读变量 readonly PI=3.14159 # PI=3 # 会报错:readonly variable bash
常见错误
name = "张运维"(等号两侧有空格)会报错,Shell 会将 name 当作命令。定义变量时等号两侧绝对不能有空格!变量类型声明(declare)
declare -i num=10 # 整数型 declare -r CONST="abc" # 只读 declare -x MYVAR="val" # 导出为环境变量(等同于 export) declare -a arr # 普通数组 declare -A map # 关联数组(字典) bash
2️⃣ 环境变量 vs 局部变量
# 局部变量:只在当前 Shell/脚本中有效 my_var="local value" # 环境变量:导出到子进程 export MY_ENV="exported" # 查看所有环境变量 env printenv # 常用内置环境变量 echo $PATH # 命令搜索路径 echo $HOME # 用户家目录 echo $USER # 当前用户名 echo $SHELL # 当前 Shell 路径 echo $PWD # 当前工作目录 echo $HOSTNAME # 主机名 bash
3️⃣ 字符串操作(核心重点)
字符串截取
str="Hello, Shell World!" # 获取长度 echo ${#str} # 19 # 截取:${变量:起始位置:长度} echo ${str:0:5} # Hello echo ${str:7} # Shell World!(从第7个到末尾) echo ${str: -6} # orld!(从倒数第6个) bash
字符串替换与删除
filename="backup-2026-05-21.tar.gz" # 替换(第一个匹配) echo ${filename/2026/2025} # backup-2025-05-21.tar.gz # 全局替换 echo ${filename//-/_} # backup_2026_05_21.tar.gz # 删除前缀(最短匹配 #,最长匹配 ##) echo ${filename#backup-} # 2026-05-21.tar.gz echo ${filename##*.} # gz(获取最终扩展名) # 删除后缀(最短匹配 %,最长匹配 %%) echo ${filename%.*} # backup-2026-05-21.tar echo ${filename%%.*} # backup-2026-05-21(去掉所有扩展名) bash
变量默认值
# ${var:-default} 变量未设置或为空时使用默认值 echo ${name:-"未知用户"} # ${var:=default} 变量未设置时赋值并使用默认值 echo ${port:=8080} # ${var:+value} 变量已设置时才使用 value echo ${debug:+" [DEBUG] "} # ${var:?message} 变量未设置时输出错误并退出 echo ${DB_HOST:?"ERROR: 必须设置 DB_HOST 环境变量"} bash
大小写转换(Bash 4+)
str="Hello World" echo ${str^^} # HELLO WORLD(全大写) echo ${str,,} # hello world(全小写) echo ${str^} # Hello world(首字母大写) bash
4️⃣ 位置参数与特殊变量
#!/bin/bash # 假设执行:./script.sh arg1 arg2 arg3 echo "脚本名:$0" # ./script.sh echo "第1个参数:$1" # arg1 echo "第2个参数:$2" # arg2 echo "参数总数:$#" # 3 echo "所有参数:$@" # arg1 arg2 arg3 echo "所有参数:$*" # arg1 arg2 arg3(和$@区别见下文) # $@ 与 $* 的区别(在双引号内): # "$@" 每个参数单独作为一个词 -> "arg1" "arg2" "arg3" # "$*" 所有参数合并为一个词 -> "arg1 arg2 arg3" # 推荐始终使用 "$@" # shift:移位参数(每次调用,$1变为原$2,$2变为原$3...) shift echo "shift后的第1个参数:$1" # arg2 bash
5️⃣ read 交互输入
#!/bin/bash # 基本读取 read -p "请输入用户名: " username echo "你好, $username!" # 读取密码(不回显) read -sp "请输入密码: " password echo # 换行 # 超时设置 read -t 10 -p "10秒内确认 [y/N]: " confirm # 读取多个变量 read -p "输入姓名和年龄(空格分隔): " name age # 读取整行,保留空格 read -r line # 逐行读取文件 while IFS= read -r line; do echo "行内容: $line" done < /etc/hosts bash
6️⃣ 输入输出重定向
# 标准输入 stdin = 文件描述符 0 # 标准输出 stdout = 文件描述符 1 # 标准错误 stderr = 文件描述符 2 # 输出重定向(覆盖) echo "hello" > output.txt # 输出重定向(追加) echo "world" >> output.txt # 错误重定向 ls /nonexist 2> error.log # 同时重定向标准输出和标准错误到同一文件 command > all.log 2>&1 # Bash 4+ 简写 command &> all.log # 丢弃输出(/dev/null 是黑洞设备) command >/dev/null 2>&1 # 输入重定向 sort < input.txt # Here Document(内嵌多行文本) cat << EOF 第一行 第二行 第三行 EOF # Here String(单行字符串作为输入) grep "hello" <<< "hello world" bash
管道(Pipeline)
# 管道:将前一个命令的 stdout 作为后一个命令的 stdin ps aux | grep nginx | grep -v grep # 统计活跃连接数 ss -tn | grep ESTABLISHED | wc -l # 按磁盘用量排序 du -sh /var/* | sort -rh | head -10 # tee:同时输出到文件和终端 df -h | tee disk_report.txt # xargs:将stdin转为命令参数 find . -name "*.log" | xargs rm -f bash
🏋️ 今日练习
- 写一个脚本,接收文件名参数,判断文件是否存在,并输出文件行数、大小、最后修改时间
- 练习字符串操作:给定路径
/home/user/backup-2026-05-21.tar.gz,分别提取文件名、扩展名、目录名 - 写一个交互式脚本,读取用户输入的服务器 IP、用户名、密码,输出连接信息(密码用 * 掩码)
- 使用 Here Document 生成一个 nginx 配置文件模板,其中服务器名和端口从变量中读取
- 将
/var/log/syslog(或/var/log/messages)中包含 "error" 的行统计数量,重定向到 error_count.txt