本文目录导读:

我来详细介绍Shell脚本迁移用户家目录的方法。
基本迁移流程
单用户迁移脚本
#!/bin/bash
# 迁移单个用户家目录
migrate_user_home() {
local username=$1
local new_base_dir=$2
# 检查用户是否存在
if ! id "$username" &>/dev/null; then
echo "错误: 用户 $username 不存在"
return 1
fi
# 获取当前家目录
local old_home=$(eval echo ~$username)
local new_home="${new_base_dir}/${username}"
# 检查新目录是否已存在
if [ -d "$new_home" ]; then
echo "警告: 新家目录 $new_home 已存在"
read -p "是否覆盖? (y/n): " confirm
if [ "$confirm" != "y" ]; then
return 1
fi
fi
# 创建新家目录
mkdir -p "$new_home"
# 复制文件(保留权限和时间戳)
echo "正在复制文件到 $new_home ..."
cp -rp "$old_home/"* "$new_home/"
cp -rp "$old_home/."* "$new_home/" 2>/dev/null # 复制隐藏文件
# 设置正确的所有权
chown -R "$username:$(id -gn $username)" "$new_home"
# 更新用户家目录配置
usermod -d "$new_home" "$username"
echo "用户 $username 的家目录已从 $old_home 迁移到 $new_home"
}
# 使用示例
# migrate_user_home "username" "/data/home"
批量迁移脚本
#!/bin/bash
# 批量迁移家目录
batch_migrate_home() {
local old_base_dir=$1
local new_base_dir=$2
local exclude_users=("root" "nobody" "systemd-coredump")
echo "开始批量迁移家目录..."
echo "从: $old_base_dir"
echo "到: $new_base_dir"
# 获取所有用户列表
for user_dir in "$old_base_dir"/*/; do
local username=$(basename "$user_dir")
# 跳过排除的用户
if [[ " ${exclude_users[@]} " =~ " ${username} " ]]; then
echo "跳过用户: $username"
continue
fi
# 检查是否为系统用户
local uid=$(id -u "$username" 2>/dev/null)
if [ "$uid" -gt 1000 ] 2>/dev/null; then # 只迁移普通用户
echo "正在迁移用户: $username"
migrate_user_home "$username" "$new_base_dir"
fi
done
}
# 使用示例
# batch_migrate_home "/home" "/data/home"
完整版迁移脚本
#!/bin/bash
# 配置
BACKUP_DIR="/backup/home_$(date +%Y%m%d)"
LOG_FILE="/var/log/home_migration_$(date +%Y%m%d_%H%M%S).log"
EXCLUDE_USERS=("root" "nobody" "systemd-coredump" "games")
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# 错误处理
error_exit() {
log "错误: $1"
exit 1
}
# 备份家目录
backup_home() {
local username=$1
local old_home=$2
log "正在备份 $username 的家目录..."
mkdir -p "$BACKUP_DIR"
# 创建备份(使用tar或rsync)
if command -v rsync &>/dev/null; then
rsync -a --delete "$old_home/" "$BACKUP_DIR/$username/"
else
tar -czf "$BACKUP_DIR/${username}_home_backup.tar.gz" -C "$(dirname $old_home)" "$(basename $old_home)"
fi
log "备份完成: $BACKUP_DIR/$username"
}
# 检查磁盘空间
check_disk_space() {
local new_home=$1
local required_space=$2
local available_space=$(df "$new_home" | awk 'NR==2 {print $4}')
if [ "$available_space" -lt "$required_space" ]; then
return 1
fi
return 0
}
# 迁移用户家目录(完整版)
migrate_user_home_advanced() {
local username=$1
local new_base_dir=$2
log "开始迁移用户: $username"
# 检查用户
if ! id "$username" &>/dev/null; then
log "用户 $username 不存在"
return 1
fi
# 获取用户信息
local old_home=$(grep "^${username}:" /etc/passwd | cut -d: -f6)
local user_shell=$(grep "^${username}:" /etc/passwd | cut -d: -f7)
local uid=$(id -u "$username")
local gid=$(id -g "$username")
log "当前家目录: $old_home"
log "用户Shell: $user_shell"
# 检查是否是系统用户
if [ "$uid" -lt 1000 ]; then
log "跳过系统用户: $username"
return 1
fi
# 新家目录路径
local new_home="${new_base_dir}/${username}"
# 检查磁盘空间
local old_size=$(du -s "$old_home" 2>/dev/null | awk '{print $1}')
if ! check_disk_space "$new_base_dir" "$old_size"; then
log "错误: 目标磁盘空间不足"
return 1
fi
# 备份
if ! backup_home "$username" "$old_home"; then
log "备份失败,取消迁移"
return 1
fi
# 检查是否有用户正在登录
if who | grep -q "^$username "; then
log "警告: 用户 $username 当前已登录"
read -p "是否继续? (y/n): " confirm
if [ "$confirm" != "y" ]; then
return 1
fi
fi
# 创建新目录
mkdir -p "$new_home"
# 使用rsync复制(更快更可靠)
if command -v rsync &>/dev/null; then
log "使用rsync复制文件..."
rsync -a --progress "$old_home/" "$new_home/" 2>&1 | tee -a "$LOG_FILE"
else
log "使用cp复制文件..."
cp -rp "$old_home/"* "$new_home/"
cp -rp "$old_home/."* "$new_home/" 2>/dev/null
fi
# 检查复制是否成功
if [ $? -ne 0 ]; then
log "文件复制失败"
return 1
fi
# 设置权限
chown -R "$uid:$gid" "$new_home"
chmod --reference="$old_home" "$new_home"
# 更新系统配置
usermod -d "$new_home" -m "$username" 2>/dev/null || {
log "警告: usermod失败,尝试直接修改passwd文件"
# 直接修改passwd文件(谨慎操作)
sed -i "s|^${username}:.*:${uid}:${gid}:.*:${old_home}:.*|${username}:x:${uid}:${gid}:${username}:${new_home}:${user_shell}|" /etc/passwd
}
log "用户 $username 迁移完成"
log "旧家目录: $old_home"
log "新家目录: $new_home"
return 0
}
# 清理临时文件
cleanup() {
log "清理临时文件..."
# 可以添加清理逻辑
}
# 主函数
main() {
local mode=$1
case $mode in
"single")
read -p "输入用户名: " username
read -p "输入新家目录基路径: " new_base
migrate_user_home_advanced "$username" "$new_base"
;;
"batch")
read -p "输入旧家目录基路径 [/home]: " old_base
old_base=${old_base:-/home}
read -p "输入新家目录基路径: " new_base
log "批量迁移模式: $old_base -> $new_base"
# 获取所有用户列表
for user_dir in "$old_base"/*/; do
[ -d "$user_dir" ] || continue
local username=$(basename "$user_dir")
# 跳过排除用户
if [[ " ${EXCLUDE_USERS[@]} " =~ " ${username} " ]]; then
log "跳过用户: $username"
continue
fi
migrate_user_home_advanced "$username" "$new_base"
done
;;
"rollback")
read -p "输入需要回滚的用户名: " username
# 回滚逻辑
log "回滚功能待实现"
;;
*)
echo "用法: $0 {single|batch|rollback}"
echo " single - 迁移单个用户"
echo " batch - 批量迁移所有用户"
echo " rollback- 回滚迁移"
exit 1
;;
esac
cleanup
log "迁移完成,日志文件: $LOG_FILE"
}
# 执行主函数
main "$@"
使用示例
基本使用
# 迁移单个用户 ./migrate_home.sh single # 批量迁移 ./migrate_home.sh batch
命令行直接调用
# 单用户迁移 migrate_user_home "john" "/data/home" # 批量迁移 batch_migrate_home "/home" "/data/home"
重要注意事项
-
备份优先
- 迁移前务必备份原家目录
- 保留数据完整性
-
用户权限
- 需要root权限执行
- 确保目标目录权限正确
-
服务停止
# 迁移前停止相关服务 systemctl stop user-runtime-dir@$UID.service systemctl stop user@$UID.service
-
验证迁移
# 验证文件完整性 diff -r old_home new_home # 检查用户设置 getent passwd username id username
-
回滚方案
# 从备份恢复 systemctl stop user@$UID.service usermod -d /old/home username cp -rp /backup/home/username/* /old/home/ chown -R username:username /old/home
这个脚本提供了完整的家目录迁移功能,包括备份、迁移、验证和回滚机制,实际使用时请根据具体环境调整参数。