shell脚本02 |批量打包镜像到/opt/docker-test-images |批量传输服务器a镜像tar包到远程服务器b | 批量从本地指定目录解压镜像tar包到服务器 |显示当前镜像列表
家人们,咱又写了综合脚本 其实4个脚本可以拆开来单独用,只是我嫌弃脚本太多,
给合并了
vim 1.sh
chmod +x 1.sh
./1.sh
Docker镜像高级管理工具
1. 批量打包镜像到/opt/docker-test-images
2. 传输镜像包到远程服务器
3. 从目录导入镜像包
4. 显示当前镜像列表
#!/bin/bash
# Docker镜像高级管理工具增强版
# 修复循环逻辑,增强进度显示和错误处理
set -u pipefail
trap \'echo -e \"\\n操作被中断\"; exit 1\' SIGINT
# 全局变量
IMAGE_DIR=\"/opt/docker-test-images\" # 默认镜像存储目录
# 显示当前镜像列表(带序号)
function display_images() {
echo -e \"\\033[36m当前Docker镜像列表:\\033[0m\"
docker images --format \"table {{.Repository}}\\t{{.Tag}}\\t{{.ID}}\\t{{.Size}}\" | awk \'NR==1 {print \"序号\\t\"$0} NR>1 {print NR-1\"\\t\"$0}\'
}
# 创建镜像存储目录
function create_image_dir() {
echo -e \"\\n\\033[33m正在创建镜像存储目录...\\033[0m\"
sudo mkdir -p \"$IMAGE_DIR\"
sudo chmod 777 \"$IMAGE_DIR\" # 确保有写入权限
echo -e \"\\033[32m✓ 目录已创建: $IMAGE_DIR\\033[0m\"
}
# 选项1:批量打包镜像(支持按序号选择)
function batch_export_images() {
create_image_dir
echo -e \"\\n\\033[36m===== 镜像列表 =====\\033[0m\"
# 获取带序���的镜像列表
mapfile -t all_images < <(docker images --format \"{{.Repository}}:{{.Tag}}\" | grep -v \"REPOSITORY:TAG\")
local index=1
declare -A image_map
# 显示可导出的镜像列表
for full_image in \"${all_images[@]}\"; do
if [[ -z \"$full_image\" || \"$full_image\" == \":\" ]]; then
continue
fi
echo -e \"${index}) \\033[33m${full_image}\\033[0m\"
image_map[\"$index\"]=\"$full_image\"
((index++))
done
# 用户第一次选择镜像
read -p \"请输入要导出的镜像序号(支持格式: 1,3,5-8,留空导出全部): \" selection
declare -a first_selected_images
if [[ -z \"$selection\" ]]; then
first_selected_images=(\"${all_images[@]}\")
echo -e \"\\033[32m✓ 将导出全部镜像(共${#all_images[@]}个)\\033[0m\"
else
declare -a selected_indices
IFS=\',\' read -ra parts <<< \"$selection\"
for part in \"${parts[@]}\"; do
if [[ $part =~ ^[0-9]+$ ]]; then
selected_indices+=(\"$part\")
elif [[ $part =~ ^([0-9]+)-([0-9]+)$ ]]; then
start=${BASH_REMATCH[1]}
end=${BASH_REMATCH[2]}
for ((i=start; i<=end; i++)); do
selected_indices+=(\"$i\")
done
fi
done
for i in \"${selected_indices[@]}\"; do
if [[ -n \"${image_map[$i]}\" ]]; then
first_selected_images+=(\"${image_map[$i]}\")
echo -e \"已选择: \\033[33m${image_map[$i]}\\033[0m\"
else
echo -e \"\\033[31m✗ 无效序号: $i\\033[0m\"
fi
done
fi
if [[ ${#first_selected_images[@]} -eq 0 ]]; then
echo -e \"\\033[31m✗ 未选择任何镜像,操作取消\\033[0m\"
return
fi
# 显示“待导出列表”带序号
echo -e \"\\n\\033[36m待导出镜像列表(再次编号):\\033[0m\"
for idx in \"${!first_selected_images[@]}\"; do
echo \"$((idx+1))) ${first_selected_images[$idx]}\"
done
# 第二次输入序号
read -p \"请再次输入要实际导出的镜像序号(如1,3,5,留空全部): \" second_selection
declare -a selected_images
if [[ -z \"$second_selection\" ]]; then
selected_images=(\"${first_selected_images[@]}\")
else
IFS=\',\' read -ra second_indices <<< \"$second_selection\"
for i in \"${second_indices[@]}\"; do
if [[ \"$i\" =~ ^[0-9]+$ ]] && (( i >= 1 )) && (( i <= ${#first_selected_images[@]} )); then
selected_images+=(\"${first_selected_images[$((i-1))]}\")
else
echo -e \"\\033[33m警告: 无效序号 $i 被忽略\\033[0m\"
fi
done
fi
if [[ ${#selected_images[@]} -eq 0 ]]; then
echo -e \"\\033[31m✗ 未选择任何镜像,操作取消\\033[0m\"
return
fi
# 初始化计数器
local exported_count=0
local skipped_count=0
total=${#selected_images[@]}
current=1
echo -e \"\\n\\033[36m===== 开始导出 ($total个镜像) =====\\033[0m\"
for full_image in \"${selected_images[@]}\"; do
image_name=\"${full_image%:*}\"
tag=\"${full_image#*:}\"
safe_name=$(echo \"$image_name\" | sed \'s|[^a-zA-Z0-9._-]|-|g\')
filename=\"${safe_name}-${tag}.tar.gz\"
filepath=\"${IMAGE_DIR}/${filename}\"
if [[ -f \"$filepath\" ]]; then
echo -e \"\\033[33m↺ ($current/$total) 跳过已存在: ${filename}\\033[0m\"
((skipped_count++))
((current++))
continue
fi
echo -e \"\\033[34m➤ ($current/$total) 打包中: $full_image → $filename\\033[0m\"
{
if docker save \"$full_image\" | gzip > \"$filepath\"; then
echo -e \"\\033[32m✓ ($current/$total) 打包成功: $filename\\033[0m\"
((exported_count++))
else
echo -e \"\\033[31m✗ ($current/$total) 打包失败: $full_image\\033[0m\"
fi
} || {
echo -e \"\\033[41m[ERROR] docker save/gzip 执行异常,退出循环 current=$current\\033[0m\"
}
((current++))
done
{
echo -e \"\\n\\033[35m===== 操作完成! =====\\033[0m\"
echo \"成功打包: $exported_count 个镜像\"
echo \"跳过重复: $skipped_count 个镜像\"
echo \"所有镜像包存储在: $IMAGE_DIR\"
} || {
echo -e \"\\033[41m[ERROR] 收尾统计语句执行异常\\033[0m\"
}
}
# 选项2:传输镜像包(增强版)
function transfer_images() {
# 允许自定义目录
read -p \"请输入镜像包目录路径(默认$IMAGE_DIR): \" transfer_dir
transfer_dir=${transfer_dir:-$IMAGE_DIR}
if [[ ! -d \"$transfer_dir\" ]]; then
echo -e \"\\033[31m目录不存在: $transfer_dir\\033[0m\"
return 1
fi
echo -e \"\\n\\033[36m$transfer_dir 中的镜像包:\\033[0m\"
# 获取镜像包列表
mapfile -t packages < /dev/null)
if [[ ${#packages[@]} -eq 0 ]]; then
echo -e \"\\033[31m未找到任何镜像包\\033[0m\"
return 1
fi
# 显示带序号列表
for i in \"${!packages[@]}\"; do
echo \"$((i+1))) ${packages[$i]##*/}\"
done
# 第一次选择包
read -p \"请选择要传输的镜像包序号(多个用逗号分隔,如1,3,5,留空全部): \" selected
declare -a first_selected_packages
if [[ -z \"$selected\" ]]; then
first_selected_packages=(\"${packages[@]}\")
else
IFS=\',\' read -ra selections <<< \"$selected\"
for sel in \"${selections[@]}\"; do
index=$((sel-1))
if [[ $index -ge 0 && $index -lt ${#packages[@]} ]]; then
first_selected_packages+=(\"${packages[$index]}\")
echo \"已选择: ${packages[$index]##*/}\"
fi
done
fi
if [[ ${#first_selected_packages[@]} -eq 0 ]]; then
echo -e \"\\033[31m未选择任��镜像包,操作取消\\033[0m\"
return 1
fi
# 显示“待传输列表”带序号
echo -e \"\\n\\033[36m待传输镜像包列表(再次编号):\\033[0m\"
for idx in \"${!first_selected_packages[@]}\"; do
echo \"$((idx+1))) ${first_selected_packages[$idx]##*/}\"
done
# 第二次输入序号
read -p \"请再次输入要实际传输的镜像包序号(如1,3,5,留空全部): \" second_selected
declare -a selected_packages
if [[ -z \"$second_selected\" ]]; then
selected_packages=(\"${first_selected_packages[@]}\")
else
IFS=\',\' read -ra second_indices <<< \"$second_selected\"
for i in \"${second_indices[@]}\"; do
idx=$((i-1))
if [[ $idx -ge 0 && $idx -lt ${#first_selected_packages[@]} ]]; then
selected_packages+=(\"${first_selected_packages[$idx]}\")
else
echo -e \"\\033[33m警告: 无效序号 $i 被忽略\\033[0m\"
fi
done
fi
if [[ ${#selected_packages[@]} -eq 0 ]]; then
echo -e \"\\033[31m未选择任何镜像包,操作取消\\033[0m\"
return 1
fi
# 获取目标信息
read -p \"请输入目标服务器IP地址: \" target_ip
read -p \"请输入目标服务器用户名(默认root): \" username
username=${username:-root}
read -p \"请输入目标路径(默认/tmp): \" target_path
target_path=${target_path:-/tmp}
# 安全读取密码
echo -n \"请输入SSH密码(输入时不会显示): \"
read -s sshpass
echo
# 设置环境变量(临时)
export SSHPASS=\"$sshpass\"
# 传输文件(带进度显示)
transfer_success=0
transfer_fail=0
total=${#selected_packages[@]}
current=1
echo -e \"\\n\\033[33m开始传输文件到 ${username}@${target_ip}:${target_path} ...\\033[0m\"
for pkg in \"${selected_packages[@]}\"; do
filename=$(basename \"$pkg\")
echo -n \"($current/$total) ��输: $filename ... \"
if sshpass -e scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \"$pkg\" \"${username}@${target_ip}:${target_path}/\"; then
echo -e \"\\033[32m成功\\033[0m\"
((transfer_success++))
else
echo -e \"\\033[31m失败\\033[0m\"
((transfer_fail++))
fi
((current++))
done
# 清理环境变量
unset SSHPASS
unset sshpass
echo -e \"\\n\\033[35m传输完成!\\033[0m\"
echo \"成功传输: $transfer_success 个文件\"
echo \"失败传输: $transfer_fail 个文件\"
echo -e \"\\n\\033[35m 注意如果报错 执行下 yum install -y sshpass 确认服务器是否安装sshpass这个命令 \\033[0m\"
# echo -e \"\\n\\033[35m 代码 UserKnownHostsFile=/dev/null 的意思是自动跳过 yes/no 询问 原因是如果目标主机第一次连接会出现如下提示:Are you sure you want to continue connecting (yes/no)? yes 那么需要跳过这个不然脚本会卡住 代码StrictHostKeyChecking=no 在脚本和自动化场景中,特别是在持续集成/持续部署(CI/CD)流程中,通常会使用StrictHostKeyChecking=no来实现无人值守的连接 \\033[0m\"
}
# 选项3:导入镜像包(增强版)
function import_images() {
# 保持原有目录选择逻辑
read -p \"请输入镜像包目录路径(默认$IMAGE_DIR): \" import_dir
import_dir=${import_dir:-$IMAGE_DIR}
if [[ ! -d \"$import_dir\" ]]; then
echo -e \"\\033[31m目录不存在: $import_dir\\033[0m\"
return 1
fi
echo -e \"\\n\\033[36m$import_dir 中的镜像包:\\033[0m\"
# 获取镜像包列表
mapfile -t packages < /dev/null)
if [[ ${#packages[@]} -eq 0 ]]; then
echo -e \"\\033[31m未找到任何镜像包\\033[0m\"
return 1
fi
# 显示带序号列表
for i in \"${!packages[@]}\"; do
echo \"$((i+1))) ${packages[$i]##*/}\"
done
# 第一次选择包
read -p \"请选择要导入的镜像包序号(多个用逗号分隔,如1,3,5,留空全部): \" selected
declare -a first_selected_packages
if [[ -z \"$selected\" ]]; then
first_selected_packages=(\"${packages[@]}\")
else
IFS=\',\' read -ra selections <<< \"$selected\"
for sel in \"${selections[@]}\"; do
index=$((sel-1))
if [[ $index -ge 0 && $index -lt ${#packages[@]} ]]; then
first_selected_packages+=(\"${packages[$index]}\")
echo \"已选择: ${packages[$index]##*/}\"
fi
done
fi
if [[ ${#first_selected_packages[@]} -eq 0 ]]; then
echo -e \"\\033[31m未选择任何镜像包,操作取消\\033[0m\"
return 1
fi
# 显示“待导入列表”带序号
echo -e \"\\n\\033[36m待导入镜像包列表(再次编号):\\033[0m\"
for idx in \"${!first_selected_packages[@]}\"; do
echo \"$((idx+1))) ${first_selected_packages[$idx]##*/}\"
done
# 第二次输入序号
read -p \"请再次输入要实际导入的镜像包序号(如1,3,5,留空全部): \" second_selected
declare -a selected_packages
if [[ -z \"$second_selected\" ]]; then
selected_packages=(\"${first_selected_packages[@]}\")
else
IFS=\',\' read -ra second_indices <<< \"$second_selected\"
for i in \"${second_indices[@]}\"; do
idx=$((i-1))
if [[ $idx -ge 0 && $idx -lt ${#first_selected_packages[@]} ]]; then
selected_packages+=(\"${first_selected_packages[$idx]}\")
else
echo -e \"\\033[33m警告: 无效序号 $i 被忽略\\033[0m\"
fi
done
fi
if [[ ${#selected_packages[@]} -eq 0 ]]; then
echo -e \"\\033[31m未选择任何镜像包,操作取消\\033[0m\"
return 1
fi
# 导入镜像(带进度显示)
import_success=0
import_fail=0
total=${#selected_packages[@]}
current=1
echo -e \"\\n\\033[33m开始导入镜像...\\033[0m\"
for pkg in \"${selected_packages[@]}\"; do
filename=$(basename \"$pkg\")
echo -n \"($current/$total) 导入: $filename ... \"
{
if gunzip -c \"$pkg\" | docker load; then
echo -e \"\\033[32m成功\\033[0m\"
((import_success++))
else
echo -e \"\\033[31m失败\\033[0m\"
((import_fail++))
fi
} || {
echo -e \"\\033[41m[ERROR] gunzip/docker load 执行异常,退出循环 current=$current\\033[0m\"
}
((current++))
done
{
echo -e \"\\n\\033[35m导入完成!\\033[0m\"
echo \"成功导入: $import_success 个镜像\"
echo \"失败导入: $import_fail 个镜像\"
} || {
echo -e \"\\033[41m[ERROR] 收尾统计语句执行异常\\033[0m\"
}
}
# 主菜单
function main_menu() {
while true; do
echo -e \"\\n\\033[1;36mDocker镜像高级管理工具\\033[0m\"
echo \"1. 批量打包镜像到/opt/docker-test-images\"
echo \"2. 传输镜像包到远程服务器\"
echo \"3. 从目录导入镜像包\"
echo \"4. 显示当前镜像列表\"
echo \"5. 退出\"
read -p \"请选择操作: \" choice
case $choice in
1) batch_export_images ;;
2) transfer_images ;;
3) import_images ;;
4) display_images ;;
5) echo \"退出程序\"; exit 0 ;;
*) echo -e \"\\033[31m无效选择,请重新输入\\033[0m\" ;;
esac
done
}
# 启动程序
main_menu