掌握Shell脚本中的变量使用技巧
从基础定义到高级应用
Shell变量是用于存储数据的命名实体,是Shell脚本编程的基础组成部分。通过使用变量,我们可以:
保存字符串、数字、文件名等各类数据,供脚本后续使用。
通过修改变量值,可以轻松改变脚本行为,无需修改代码。
将常用值定义为变量,便于统一管理和修改。
通过变量接收用户输入,使脚本更加灵活和交互式。
NAME 和 name 是不同的变量)Shell中有多种类型的变量,每种类型有不同的作用域和生命周期。
在当前Shell会话中定义的变量,只在当前脚本或Shell会话中可见。
# 定义局部变量
name="Alice"
age=25
# 使用变量
echo "Name: $name, Age: $age"
# 变量只在当前Shell中有效
# 在子Shell或新终端中不可见
在整个Shell会话及其子进程中可见的变量,通常用于系统配置。
# 定义环境变量
export DATABASE_URL="postgres://user:pass@localhost/db"
export PATH="$PATH:/usr/local/bin"
# 查看环境变量
echo $PATH
env | grep DATABASE
# 环境变量对子进程可见
由Shell自动设置的特殊变量,提供脚本执行的相关信息。
# 特殊变量示例
echo "脚本名称: $0"
echo "第一个参数: $1"
echo "所有参数: $@"
echo "参数个数: $#"
echo "进程ID: $$"
echo "退出状态: $?"
可以存储多个值的变量,通过索引访问各个元素。
# 定义数组
fruits=("apple" "banana" "orange")
# 访问数组元素
echo "第一个水果: ${fruits[0]}"
echo "所有水果: ${fruits[@]}"
echo "水果数量: ${#fruits[@]}"
# 添加元素
fruits+=("grape")
| 变量类型 | 作用域 | 定义方式 | 示例 |
|---|---|---|---|
| 局部变量 | 当前Shell | var=value |
name="Alice" |
| 环境变量 | 当前Shell及子进程 | export var=value |
export PATH="/usr/bin" |
| 只读变量 | 当前Shell | readonly var=value |
readonly PI=3.14 |
| 位置参数 | 当前脚本 | 自动设置 | $1, $2, $@ |
学习如何定义、使用和操作Shell变量。
在Shell中定义变量并为其赋值。
#!/bin/bash
# 基本变量赋值(等号两边不能有空格)
name="Alice"
age=25
price=19.99
# 使用命令输出赋值
current_date=$(date)
file_count=$(ls | wc -l)
# 使用引号处理包含空格的字符串
message="Hello, World!"
file_path="/home/user/documents/file name.txt"
# 只读变量(不可修改)
readonly PI=3.14159
# 删除变量
unset name
name="Alice"(正确) vs name = "Alice"(错误)$(command)或反引号`command`获取命令输出使用变量值并进行各种扩展操作。
#!/bin/bash
name="Alice"
path="/home/user/file.txt"
# 基本引用
echo "Hello, $name"
echo "Path: $path"
# 使用大括号明确变量边界
echo "Hello, ${name}"
echo "File: ${path}.bak"
# 当变量名后紧跟其他字符时必须使用大括号
echo "Hello, ${name}smith" # 正确
echo "Hello, $namesmith" # 错误(会尝试访问namesmith变量)
# 变量默认值
echo "Username: ${USER:-guest}"
echo "Home: ${HOME:-/tmp}"
# 检查变量是否设置
echo "Name is set: ${name+yes}"
echo "Undefined is set: ${undefined+no}"
对字符串变量进行各种操作和处理。
#!/bin/bash
text="Hello, World! This is a test string."
# 字符串长度
echo "Length: ${#text}"
# 提取子字符串
echo "Substring(0,5): ${text:0:5}" # Hello
echo "Substring(7): ${text:7}" # World! This is a test string.
# 字符串替换
echo "Replace World: ${text/World/Shell}" # 替换第一个匹配
echo "Replace all s: ${text//s/S}" # 替换所有匹配
echo "Replace prefix: ${text/#Hello/Hi}" # 替换开头
echo "Replace suffix: ${text/%./!}" # 替换结尾
# 大小写转换
name="alice"
echo "Uppercase: ${name^^}" # ALICE
echo "Lowercase: ${name,,}" # alice (已经是小写)
# 删除匹配模式
filename="backup.tar.gz"
echo "Remove .tar.gz: ${filename%.tar.gz}" # backup
echo "Remove backup.: ${filename#backup.}" # tar.gz
Shell提供了一系列自动设置的特殊变量,用于获取脚本执行的相关信息。
| 变量 | 描述 | 示例 |
|---|---|---|
$0 |
当前脚本的名称 | script.sh |
$1, $2, ... $9 |
脚本的参数(第1-9个) | $1 是第一个参数 |
$# |
传递给脚本的参数个数 | 如果传递3个参数,$# 为3 |
$@ |
所有参数(作为单独的单词) | "$1" "$2" "$3" ... |
$* |
所有参数(作为单个单词) | "$1 $2 $3 ..." |
$? |
最后命令的退出状态 | 0表示成功,非0表示错误 |
$$ |
当前Shell的进程ID | 脚本运行的进程ID |
$! |
最后执行的后台进程的PID | 后台进程的进程ID |
#!/bin/bash
# 特殊变量示例脚本
echo "脚本名称: $0"
echo "参数个数: $#"
echo "所有参数: $@"
# 处理参数
if [ $# -gt 0 ]; then
echo "第一个参数: $1"
echo "第二个参数: $2"
fi
# 使用shift处理多个参数
echo "--- 使用shift处理参数 ---"
count=1
while [ $# -gt 0 ]; do
echo "参数 $count: $1"
shift
count=$((count + 1))
done
# 检查命令执行状态
ls /nonexistent > /dev/null 2>&1
echo "ls命令退出状态: $?"
# 显示进程信息
echo "当前进程ID: $$"
echo "父进程ID: $PPID"
# 后台进程示例
sleep 10 &
echo "后台进程ID: $!"
$@ 与 $* 的区别:在引号内使用时,"$@" 将每个参数作为独立的单词处理,而 "$*" 将所有参数作为一个单词处理。在循环处理参数时,通常使用 "$@"。
Shell支持一维数组,可以存储多个值并通过索引访问。
#!/bin/bash
# 定义数组的不同方式
fruits=("apple" "banana" "orange")
colors=(red green blue)
numbers=({1..5}) # 1 2 3 4 5
# 逐个元素赋值
days[0]="Monday"
days[1]="Tuesday"
days[2]="Wednesday"
# 访问数组元素
echo "第一个水果: ${fruits[0]}"
echo "第二个颜色: ${colors[1]}"
echo "所有数字: ${numbers[@]}"
# 数组长度
echo "水果数量: ${#fruits[@]}"
echo "颜色数量: ${#colors[@]}"
# 遍历数组
echo "--- 所有水果 ---"
for fruit in "${fruits[@]}"; do
echo "水果: $fruit"
done
echo "--- 带索引遍历 ---"
for i in "${!fruits[@]}"; do
echo "索引 $i: ${fruits[$i]}"
done
#!/bin/bash
# 初始化数组
items=("one" "two" "three" "four" "five")
# 添加元素
items+=("six")
items=("${items[@]}" "seven")
# 删除元素
unset items[2] # 删除第三个元素("three")
echo "删除后: ${items[@]}"
# 重新索引数组(删除元素后索引可能不连续)
items=("${items[@]}")
echo "重新索引后: ${items[@]}"
# 数组切片
echo "前三个元素: ${items[@]:0:3}"
echo "从第二个开始: ${items[@]:2}"
# 数组合并
array1=("A" "B" "C")
array2=("D" "E" "F")
combined=("${array1[@]}" "${array2[@]}")
echo "合并数组: ${combined[@]}"
# 将命令输出转换为数组
files=($(ls *.txt 2>/dev/null))
echo "文本文件: ${files[@]}"
# 关联数组(Bash 4.0+)
declare -A user
user[name]="Alice"
user[age]=25
user[city]="Beijing"
echo "用户名: ${user[name]}"
echo "用户年龄: ${user[age]}"
通过实际脚本示例展示Shell变量的综合应用。
使用各种变量类型收集和显示系统信息。
#!/bin/bash
# 系统信息收集脚本
# 定义颜色变量(用于输出美化)
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 系统信息变量
hostname=$(hostname)
os_name=$(grep PRETTY_NAME /etc/os-release | cut -d= -f2 | tr -d '"')
kernel_version=$(uname -r)
architecture=$(uname -m)
# 用户信息变量
current_user=$(whoami)
user_home=$HOME
shell_type=$SHELL
# 系统资源变量
memory_info=$(free -h | grep Mem | awk '{print $2}')
disk_usage=$(df -h / | awk 'NR==2 {print $5}')
load_average=$(uptime | awk -F'load average:' '{print $2}')
# 当前时间变量
current_time=$(date '+%Y-%m-%d %H:%M:%S')
uptime_info=$(uptime -p)
# 输出系统信息
echo -e "${BLUE}=== 系统信息 ===${NC}"
echo -e "主机名: ${GREEN}$hostname${NC}"
echo -e "操作系统: ${GREEN}$os_name${NC}"
echo -e "内核版本: ${GREEN}$kernel_version${NC}"
echo -e "系统架构: ${GREEN}$architecture${NC}"
echo -e "\n${BLUE}=== 用户信息 ===${NC}"
echo -e "当前用户: ${GREEN}$current_user${NC}"
echo -e "用户家目录: ${GREEN}$user_home${NC}"
echo -e "Shell类型: ${GREEN}$shell_type${NC}"
echo -e "\n${BLUE}=== 资源使用 ===${NC}"
echo -e "内存总量: ${GREEN}$memory_info${NC}"
echo -e "根分区使用率: ${GREEN}$disk_usage${NC}"
echo -e "系统负载: ${GREEN}$load_average${NC}"
echo -e "\n${BLUE}=== 时间信息 ===${NC}"
echo -e "当前时间: ${GREEN}$current_time${NC}"
echo -e "运行时间: ${GREEN}$uptime_info${NC}"
# 使用数组存储网络接口信息
interfaces=($(ip link show | grep -E '^[0-9]+:' | cut -d: -f2 | tr -d ' '))
echo -e "\n${BLUE}=== 网络接口 ===${NC}"
for interface in "${interfaces[@]}"; do
# 跳过lo接口
if [[ "$interface" != "lo" ]]; then
ip_addr=$(ip addr show "$interface" | grep -E 'inet ' | awk '{print $2}' | head -1)
if [ -n "$ip_addr" ]; then
echo -e "$interface: ${GREEN}$ip_addr${NC}"
else
echo -e "$interface: ${YELLOW}未分配IP${NC}"
fi
fi
done
# 脚本结束信息
echo -e "\n${GREEN}系统信息收集完成!${NC}"
${var}而不是$var提高可读性使用 set -x 开启调试模式,可以查看变量的实际展开过程。使用 echo 或 printf 输出变量值进行调试。