数组是Shell脚本中非常重要的数据结构,它允许我们在单个变量中存储多个值。通过使用数组,我们可以:
在单个变量中存储多个相关的值,如文件名列表、用户名列表等。
通过循环遍历数组,批量处理数据,提高脚本效率。
通过索引快速访问特定位置的元素,实现高效数据检索。
支持动态添加、删除和修改元素,适应不同的数据处理需求。
Shell支持两种类型的数组:索引数组(使用数字作为索引)和关联数组(使用字符串作为键名)。Bash 4.0及以上版本支持关联数组。
学习如何定义和初始化Shell数组。
使用数字作为索引的数组,索引从0开始。
#!/bin/bash
# 方法1: 直接赋值
fruits=("apple" "banana" "orange")
# 方法2: 逐个元素赋值
colors[0]="red"
colors[1]="green"
colors[2]="blue"
# 方法3: 使用declare命令
declare -a numbers=(1 2 3 4 5)
# 方法4: 从命令输出创建数组
files=($(ls *.txt))
# 方法5: 使用序列
letters=({a..e})
echo "水果数组: ${fruits[@]}"
echo "颜色数组: ${colors[@]}"
echo "数字数组: ${numbers[@]}"
echo "字母数组: ${letters[@]}"
使用字符串作为键名的数组,需要Bash 4.0+。
#!/bin/bash
# 声明关联数组
declare -A user
# 赋值方式1: 逐个赋值
user[name]="Alice"
user[age]="25"
user[city]="Beijing"
# 赋值方式2: 一次性赋值
declare -A country_codes=([CN]="China" [US]="USA" [JP]="Japan")
# 赋值方式3: 使用键值对列表
declare -A colors=(
["red"]="#FF0000"
["green"]="#00FF00"
["blue"]="#0000FF"
)
echo "用户名: ${user[name]}"
echo "国家代码: ${country_codes[CN]}"
echo "红色代码: ${colors[red]}"
关联数组需要Bash 4.0或更高版本。可以使用bash --version检查Bash版本。
学习如何访问数组元素和获取数组信息。
#!/bin/bash
# 定义数组
fruits=("apple" "banana" "orange" "grape" "mango")
# 访问单个元素
echo "第一个水果: ${fruits[0]}" # apple
echo "第二个水果: ${fruits[1]}" # banana
echo "最后一个水果: ${fruits[-1]}" # mango (Bash 4.3+)
# 访问所有元素
echo "所有水果: ${fruits[@]}" # apple banana orange grape mango
echo "所有水果: ${fruits[*]}" # apple banana orange grape mango
# 数组长度
echo "水果数量: ${#fruits[@]}" # 5
# 获取所有索引
echo "数组索引: ${!fruits[@]}" # 0 1 2 3 4
# 数组切片
echo "前三个水果: ${fruits[@]:0:3}" # apple banana orange
echo "从第二个开始: ${fruits[@]:2}" # orange grape mango
# 关联数组示例
declare -A user=([name]="Alice" [age]=25 [city]="Beijing")
echo "用户名: ${user[name]}"
echo "用户年龄: ${user[age]}"
echo "所有键: ${!user[@]}"
echo "所有值: ${user[@]}"
${array[index]}访问特定元素${array[@]}或${array[*]}访问所有元素${#array[@]}获取数组长度${!array[@]}获取所有索引/键名${array[@]:start:length}进行数组切片学习如何遍历数组的所有元素。
#!/bin/bash
# 定义数组
fruits=("apple" "banana" "orange" "grape")
echo "=== 方法1: for循环遍历值 ==="
for fruit in "${fruits[@]}"; do
echo "水果: $fruit"
done
echo -e "\n=== 方法2: for循环遍历索引 ==="
for i in "${!fruits[@]}"; do
echo "索引 $i: ${fruits[$i]}"
done
echo -e "\n=== 方法3: C风格for循环 ==="
for ((i=0; i<${#fruits[@]}; i++)); do
echo "元素 $i: ${fruits[$i]}"
done
echo -e "\n=== 方法4: while循环 ==="
i=0
while [ $i -lt ${#fruits[@]} ]; do
echo "元素 $i: ${fruits[$i]}"
((i++))
done
echo -e "\n=== 关联数组遍历 ==="
declare -A user=([name]="Alice" [age]=25 [city]="Beijing")
for key in "${!user[@]}"; do
echo "$key: ${user[$key]}"
done
echo -e "\n=== 使用while和read遍历(多行数据)==="
data=("line1" "line2" "line3")
printf "%s\n" "${data[@]}" | while read line; do
echo "处理: $line"
done
"${array[@]}"而不是"${array[*]}"来保留元素中的空格"${!array[@]}"获取所有键名"${!array[@]}"方式遍历学习如何对数组进行添加、删除、修改等操作。
#!/bin/bash
# 初始化数组
fruits=("apple" "banana" "orange")
echo "原始数组: ${fruits[@]}"
# 添加元素到末尾
fruits+=("grape")
echo "添加grape后: ${fruits[@]}"
# 添加多个元素
fruits+=("mango" "pear")
echo "添加多个后: ${fruits[@]}"
# 在特定位置插入元素(需要重新创建数组)
fruits=("${fruits[@]:0:2}" "peach" "${fruits[@]:2}")
echo "在位置2插入peach后: ${fruits[@]}"
# 修改元素
fruits[1]="blueberry"
echo "修改第二个元素后: ${fruits[@]}"
# 删除元素
unset fruits[2]
echo "删除第三个元素后: ${fruits[@]}"
echo "当前索引: ${!fruits[@]}"
# 重新索引数组
fruits=("${fruits[@]}")
echo "重新索引后: ${fruits[@]}"
echo "重新索引后的索引: ${!fruits[@]}"
# 数组合并
arr1=("A" "B" "C")
arr2=("D" "E" "F")
combined=("${arr1[@]}" "${arr2[@]}")
echo "合并数组: ${combined[@]}"
# 数组拷贝
copy=("${fruits[@]}")
echo "数组拷贝: ${copy[@]}"
# 清空数组
unset fruits
echo "清空数组后长度: ${#fruits[@]}"
#!/bin/bash
# 定义数组
numbers=(10 25 30 45 50 65 70 85 90)
echo "原始数组: ${numbers[@]}"
# 搜索元素
search_value=50
for i in "${!numbers[@]}"; do
if [ "${numbers[$i]}" -eq "$search_value" ]; then
echo "找到 $search_value 在索引 $i"
break
fi
done
# 过滤数组(大于50的值)
filtered=()
for num in "${numbers[@]}"; do
if [ "$num" -gt 50 ]; then
filtered+=("$num")
fi
done
echo "大于50的值: ${filtered[@]}"
# 数组排序
sorted=($(printf "%s\n" "${numbers[@]}" | sort -n))
echo "排序后数组: ${sorted[@]}"
# 反转数组
reversed=()
for ((i=${#numbers[@]}-1; i>=0; i--)); do
reversed+=("${numbers[$i]}")
done
echo "反转数组: ${reversed[@]}"
# 数组去重
duplicates=(1 2 2 3 4 4 5 5 5)
unique=($(printf "%s\n" "${duplicates[@]}" | sort -u))
echo "去重后数组: ${unique[@]}"
# 检查元素是否存在
check_exists() {
local array=("$@")
local seeking=$2
local in=1
for element in "${array[@]}"; do
if [[ "$element" == "$seeking" ]]; then
in=0
break
fi
done
return $in
}
if check_exists "${numbers[@]}" 30; then
echo "30 存在于数组中"
else
echo "30 不存在于数组中"
fi
通过实际脚本示例展示Shell数组的综合应用。
使用数组管理文件列表和执行批量操作。
#!/bin/bash
# 文件管理系统 - 数组应用示例
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 全局数组变量
declare -a files
declare -a backup_files
declare -A file_info
# 初始化文件列表
init_files() {
echo -e "${BLUE}扫描当前目录文件...${NC}"
files=($(ls -p | grep -v /)) # 只获取文件,排除目录
echo -e "找到 ${#files[@]} 个文件"
}
# 显示文件列表
show_files() {
echo -e "\n${BLUE}=== 文件列表 ===${NC}"
if [ ${#files[@]} -eq 0 ]; then
echo -e "${YELLOW}没有找到文件${NC}"
return
fi
for i in "${!files[@]}"; do
local file="${files[$i]}"
local size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
local date=$(date -r "$file" "+%Y-%m-%d %H:%M:%S")
# 存储文件信息到关联数组
file_info["${file}_size"]=$size
file_info["${file}_date"]=$date
printf "%-3s %-20s %-10s %s\n" "$i" "$file" "$size" "$date"
done
}
# 备份文件
backup_files() {
local backup_dir="backup_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$backup_dir"
backup_files=()
for file in "${files[@]}"; do
if cp "$file" "$backup_dir/"; then
backup_files+=("$file")
echo -e "${GREEN}备份成功: $file${NC}"
else
echo -e "${RED}备份失败: $file${NC}"
fi
done
echo -e "\n${GREEN}备份完成! 共备份 ${#backup_files[@]} 个文件到 $backup_dir${NC}"
}
# 按扩展名筛选文件
filter_by_extension() {
local extension="$1"
local filtered=()
for file in "${files[@]}"; do
if [[ "$file" == *".$extension" ]]; then
filtered+=("$file")
fi
done
if [ ${#filtered[@]} -gt 0 ]; then
echo -e "\n${BLUE}=== .$extension 文件 ===${NC}"
printf "%s\n" "${filtered[@]}"
else
echo -e "${YELLOW}没有找到 .$extension 文件${NC}"
fi
}
# 文件统计
file_statistics() {
echo -e "\n${BLUE}=== 文件统计 ===${NC}"
# 按扩展名统计
declare -A ext_count
declare -A ext_size
for file in "${files[@]}"; do
local size="${file_info[${file}_size]}"
local extension="${file##*.}"
# 如果没有扩展名,使用"无"
if [[ "$file" == "$extension" ]]; then
extension="无"
fi
((ext_count[$extension]++))
((ext_size[$extension]+=size))
done
echo -e "${GREEN}按扩展名统计:${NC}"
for ext in "${!ext_count[@]}"; do
local count=${ext_count[$ext]}
local total_size=${ext_size[$ext]}
local percentage=$((count * 100 / ${#files[@]}))
echo -e " .$ext: $count 个文件 ($total_size 字节) $percentage%"
done
# 文件大小统计
local total_size=0
for file in "${files[@]}"; do
((total_size+=${file_info[${file}_size]}))
done
echo -e "\n${GREEN}总大小: $total_size 字节${NC}"
}
# 主菜单
main_menu() {
while true; do
echo -e "\n${BLUE}=== 文件管理系统 ===${NC}"
echo "1. 显示文件列表"
echo "2. 备份所有文件"
echo "3. 筛选文本文件"
echo "4. 筛选图片文件"
echo "5. 文件统计"
echo "6. 重新扫描文件"
echo "0. 退出"
read -p "请选择操作 [0-6]: " choice
case $choice in
1) show_files ;;
2) backup_files ;;
3) filter_by_extension "txt" ;;
4) filter_by_extension "jpg" ;;
5) file_statistics ;;
6) init_files ;;
0)
echo -e "${GREEN}再见!${NC}"
break
;;
*)
echo -e "${RED}无效选择!${NC}"
;;
esac
done
}
# 脚本入口
main() {
echo -e "${GREEN}文件管理系统启动...${NC}"
init_files
main_menu
}
# 执行主函数
main "$@"
./file_manager.sh"${array[@]}"${!array[@]}获取索引进行遍历使用 declare -p array_name 查看数组的完整内容。在脚本中添加 set -x 开启调试模式,观察数组操作过程。