#!/bin/bash # ============================================ # 远程挂载管理脚本 # 功能:挂载远程CIFS/NFS共享到本地目录 # 支持挂载共享的任意子目录 # ============================================ # 配置部分 readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly REMOTE_IP="192.168.187.109" readonly BASE_DIR="$HOME/workspace" readonly CREDENTIALS="Administrator%tomypyxh" # 颜色定义 readonly COLOR_CYAN="\033[1;36m" readonly COLOR_RESET="\033[0m" # ============================================ # 函数定义 # ============================================ show_help() { echo -e "${COLOR_CYAN}用法:${COLOR_RESET}" echo -e "${COLOR_CYAN} $SCRIPT_NAME <-cifs|-nfs>${COLOR_RESET}" echo -e "${COLOR_CYAN} $SCRIPT_NAME <-cifs|-nfs> sharename${COLOR_RESET}" echo -e "${COLOR_CYAN} $SCRIPT_NAME <-cifs|-nfs> sharename[/子目录]${COLOR_RESET}" echo -e "${COLOR_CYAN} $SCRIPT_NAME <-cifs|-nfs> sharename[/子目录] 本地目录${COLOR_RESET}" echo # 暂不处理未指定协议 #echo -e "${COLOR_CYAN} $SCRIPT_NAME sharename${COLOR_RESET}" #echo -e "${COLOR_CYAN} $SCRIPT_NAME sharename[/子目录]${COLOR_RESET}" #echo -e "${COLOR_CYAN} $SCRIPT_NAME sharename[/子目录] 本地目录${COLOR_RESET}" #echo echo "参数说明:" echo " -cifs: 使用CIFS协议挂载Windows共享" echo " -nfs: 使用NFS协议挂载Linux共享" echo " sharename: 远程共享名称" echo " [/子目录]: 可选,要挂载的共享子目录路径" echo " 本地目录: 可选,本地挂载点路径" echo echo "示例:" echo " $SCRIPT_NAME -cifs sharename" echo " $SCRIPT_NAME -cifs sharename/subdir" echo " $SCRIPT_NAME -cifs sharename/subdir/deeper" echo " $SCRIPT_NAME -cifs sharename/subdir ~/myproject" #echo " $SCRIPT_NAME myshare/subdir" } # 检查命令是否存在 check_command() { if ! command -v "$1" >/dev/null 2>&1; then echo "错误: 找不到命令 '$1',请先安装" exit 1 fi } # 检查是否已挂载 is_already_mounted() { local remote_path="$1" local local_dir="$2" # 使用mapfile读取挂载信息 mapfile -t mount_info < <(sudo mount 2>/dev/null) for line in "${mount_info[@]}"; do if [[ "$line" == *"$remote_path"* && "$line" == *"$local_dir"* ]]; then return 0 fi done return 1 } # 获取远程共享列表 get_remote_shares() { local protocol="$1" case "$protocol" in cifs) check_command "smbclient" # 匹配以可选空白开头,后跟一个标识符(单词),再跟一个或多个空白,最后是 "Disk" 的行 # 获取共享列表,过滤掉系统共享(以$结尾的),删除所有空行 #sudo smbclient -U "$CREDENTIALS" -L "$REMOTE_IP" 2>/dev/null | \ # grep -E '^[[:space:]]*[A-Za-z0-9_]+[[:space:]]+Disk' | \ # awk '{print $1}' | sed '/\$$/d' | grep -v '^$' # 获取共享列表,保留系统共享(以$结尾的),删除所有空行 #sudo smbclient -U "$CREDENTIALS" -L "$REMOTE_IP" 2>/dev/null | \ # grep -E '^[[:space:]]*[A-Za-z0-9_\$]+[[:space:]]+Disk' | \ # awk '{print $1}' | sed '/\$$/d' | grep -v '^$' # 保留 C$、D$等 sudo smbclient -U "$CREDENTIALS" -L "$REMOTE_IP" 2>/dev/null | \ grep -E '^[[:space:]]*[A-Za-z0-9_\$]+[[:space:]]+Disk' | \ awk '{print $1}' | grep -v '^$' ;; nfs) check_command "showmount" sudo showmount -e "$REMOTE_IP" 2>/dev/null | \ sed "1d" | awk '{print $1}' | cut -d'/' -f2 ;; *) echo "未知协议: $protocol" return 1 ;; esac } # 验证共享是否存在(只验证共享根目录) validate_share() { local protocol="$1" local share_name="$2" # 提取共享根目录名称(第一个/之前的部分) local base_share="${share_name%%/*}" local shares mapfile -t shares < <(get_remote_shares "$protocol") for share in "${shares[@]}"; do if [[ "$share" == "$base_share" ]]; then return 0 fi done return 1 } # 解析共享名和子目录 parse_share_with_subdir() { local input="$1" local -n ref_share_name="$2" local -n ref_subdir_path="$3" # 如果包含子目录路径 if [[ "$input" == */* ]]; then ref_share_name="${input%%/*}" # 从末尾开始匹配,删除最长匹配的部分 ref_subdir_path="${input#*/}" # 从开头开始匹配,删除最短匹配的部分 # 确保共享名不为空 if [[ -z "$ref_share_name" ]]; then echo "错误: 共享名不能为空" return 1 fi else ref_share_name="$input" ref_subdir_path="" fi return 0 } # 确定本地目录 get_local_dir() { local share_name="$1" local subdir_path="$2" local user_arg="$3" if [[ -n "$user_arg" ]]; then # 用户指定了本地目录 if [[ "${user_arg:0:1}" == "/" ]]; then # 绝对路径 echo "$user_arg" else # 相对路径,基于 BASE_DIR echo "$BASE_DIR/$user_arg" fi elif [[ -n "$subdir_path" ]]; then # 如果有子目录,使用共享名+子目录名作为本地目录名,避免使用斜杠创建多层目录 # local safe_subdir="${subdir_path//\//_}" # echo "$BASE_DIR/${share_name}_${safe_subdir}" # 如果有子目录,取最后一个子目录 local array=(${subdir_path//\// }) local alen=${#array[@]} local pos=$(( pos = alen - 1 )) local tailv=${array[$pos]} echo "$BASE_DIR/${tailv}" else # 默认路径 echo "$BASE_DIR/$share_name" fi } # 构建远程路径 get_remote_path() { local protocol="$1" local share_name="$2" local subdir_path="$3" local remote_path="" if [[ "$protocol" == "cifs" ]]; then remote_path="//$REMOTE_IP/$share_name" if [[ -n "$subdir_path" ]]; then remote_path="$remote_path/$subdir_path" fi else remote_path="$REMOTE_IP:/$share_name" if [[ -n "$subdir_path" ]]; then remote_path="$remote_path/$subdir_path" fi fi echo "$remote_path" } # 获取挂载选项 get_mount_options() { local protocol="$1" case "$protocol" in cifs) # 获取当前用户的uid和gid local uid=$(id -u) local gid=$(id -g) local array=(${CREDENTIALS/\%/ }) local susername=${array[0]} local spassword=${array[1]} # CIFS挂载选项 local opt opt="username=$susername,password=$spassword" opt="$opt,vers=2.0,dir_mode=0755,file_mode=0666,uid=$uid,gid=$gid,nounix,noserverino" echo "$opt" ;; nfs) # NFS挂载选项 echo "rw,bg,retry=1" ;; *) echo "" ;; esac } # 执行挂载 mount_share() { local protocol="$1" local remote_dir="$2" local local_dir="$3" # 检查本地目录是否存在 if [[ ! -d "$local_dir" ]]; then #echo "创建目录: $local_dir" mkdir -p "$local_dir" || { echo "错误: 无法创建目录 $local_dir" return 1 } fi # 获取挂载选项 local options="$(get_mount_options "$protocol")" # 执行挂载 #echo "正在挂载: $remote_dir -> $local_dir" #echo "挂载选项: $options" if sudo mount -t "$protocol" -o "$options" "$remote_dir" "$local_dir"; then #echo "挂载成功" echo "挂载成功: $remote_dir -> $local_dir" return 0 else #echo "错误: 挂载失败" #echo "提示: 确保远程路径 $remote_dir 存在且可访问" echo "挂载失败: $remote_dir -> $local_dir" return 1 fi } # ============================================ # 主程序 # ============================================ main() { # 参数检查 if [[ "$#" -eq 0 || "$1" == "-h" || "$1" == "--help" ]]; then show_help exit 0 fi # 变量初始化 local protocol="" local share_name="" local subdir_path="" local local_dir="" local remote_dir="" #echo "pwd : $(pwd)" # 处理 -cifs/-nfs 参数 if [[ "$1" == "-cifs" || "$1" == "-nfs" ]]; then protocol="${1#-}" # 去掉开头的'-' # 参数个数检查 if [[ "$#" -gt 3 ]]; then echo "错误: 参数过多" show_help exit 1 fi if [[ "$#" -eq 1 ]]; then # 仅列出共享 echo -e "${COLOR_CYAN}可用的${protocol}共享:${COLOR_RESET}" # read -r中的-r选项的作用:原始读取,禁止反斜杠转义 get_remote_shares "$protocol" | while read -r share; do echo " $share" done #echo #echo "提示: 要挂载共享的子目录,使用: $SCRIPT_NAME -$protocol 共享名/子目录路径" exit 0 fi # 解析共享名和子目录路径 if ! parse_share_with_subdir "$2" share_name subdir_path; then exit 1 fi # 验证共享根目录是否存在 if ! validate_share "$protocol" "$share_name"; then echo "错误: ${protocol}共享 '$share_name' 不存在" echo "可用的共享有:" get_remote_shares "$protocol" | while read -r share; do echo " $share" done exit 1 fi # 确定本地目录 # ${3:-}: 如果第3个参数未设置或为空,则替换为空字符串 local_dir=$(get_local_dir "$share_name" "$subdir_path" "${3:-}") # 构建远程路径 remote_dir=$(get_remote_path "$protocol" "$share_name" "$subdir_path") else # 自动检测模式 if [[ "$#" -gt 2 ]]; then echo "错误: 参数过多" show_help exit 1 fi # 解析共享名和子目录路径 if ! parse_share_with_subdir "$1" share_name subdir_path; then exit 1 fi # 确定本地目录 local_dir="$(get_local_dir "$share_name" "$subdir_path" "${2:-}")" # 检测共享类型 local found_cifs=false local found_nfs=false if validate_share "cifs" "$share_name"; then found_cifs=true fi if validate_share "nfs" "$share_name"; then found_nfs=true fi if [[ "$found_cifs" == false && "$found_nfs" == false ]]; then echo "错误: 未找到共享 '$share_name'" exit 1 elif [[ "$found_cifs" == true && "$found_nfs" == true ]]; then echo "错误: 共享 '$share_name' 同时存在于CIFS和NFS中, 请明确指定协议" exit 1 fi # 确定协议和远程路径 if [[ "$found_cifs" == true ]]; then protocol="cifs" else protocol="nfs" fi # 构建远程路径 remote_dir=$(get_remote_path "$protocol" "$share_name" "$subdir_path") fi # 检查是否已挂载 if is_already_mounted "$remote_dir" "$local_dir"; then echo "信息: 共享已挂载到 $local_dir" exit 0 fi # 显示挂载信息 #echo "挂载信息:" #echo " 协议: $protocol" #echo " 共享: $share_name" #[[ -n "$subdir_path" ]] && echo " 子目录: $subdir_path" #echo " 远程路径: $remote_dir" #echo " 本地目录: $local_dir" #echo #exit 0 # 执行挂载 mount_share "$protocol" "$remote_dir" "$local_dir" } # 异常处理 trap 'echo "脚本被中断"; exit 1' INT TERM # 运行主程序 main "$@"