欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

macos 上没有 smbclient ,可以用其它命令代替吗 chatgpt 有大用 有大大用

是的,在 macOS 上默认是没有 smbclient 的(它是 Samba 套件的一部分,Linux 常见)。不过你仍然可以用系统自带的命令或者方式来访问 SMB 共享:

可替代方法

1. mount_smbfs

macOS 自带的挂载命令:

mkdir ~/mnt_smb   # 建立挂载点 mount_smbfs //username:password@server/share ~/mnt_smb    



示例:

mount_smbfs //user:123456@192.168.1.100/Public ~/mnt_smb    



注意:如果密码里有特殊符号,需要 URL 编码(比如 @%40)。

卸载:

umount ~/mnt_smb    





2. open (调用 Finder)

直接通过命令行调起 Finder 连接 SMB:

open "smb://username:password@server/share"    



这会在 Finder 里挂载共享盘。


3. osascript (AppleScript)

如果你想脚本化,可以用 AppleScript:

osascript -e 'mount volume "smb://username:password@server/share"'    




4. 如果真的需要 smbclient

  • 可以通过 Homebrew 安装 Samba:

    brew install samba            

                           

    然后你就能用熟悉的 smbclient 命令了。


👉 总结:

  • 如果只是想访问/挂载共享,推荐用 mount_smbfsopen smb://

  • 如果需要像 Linux smbclient 那样交互式操作(比如 ls, get, put),就要装 samba 提供的 smbclient

要不要我帮你写一个 基于 mount_smbfs 的脚本,模拟 smbclient ls/get/put 的功能?



macos 用 mount_smbfs  命令 来代替的 smbclient 的功能

👌 好的,那我们把 smbclient 逻辑改成 mount_smbfs

原理

  • mount_smbfs 会把远程 SMB 共享盘挂载到本地目录(挂载点),之后就可以像普通文件系统一样用 cprsyncmkdir 等命令操作。

  • 相比 smbclient,这种方式更直观,复制文件可以直接用 cp,也能保持目录层级。


示例脚本

#!/bin/zsh REMOTE_HOST="192.168.77.220" REMOTE_SHARE="KKKKK_Audit$" TARGET_DIR="11111111" MOUNT_POINT="/Volumes/KKKKK_Audit" SMBUSER="admin" SMBPASS="123456" # 确保挂载点存在 if [[ ! -d "$MOUNT_POINT" ]]; then    sudo mkdir -p "$MOUNT_POINT" fi # 如果没挂载,先挂载 if ! mount | grep -q "$MOUNT_POINT"; then    echo "正在挂载远程共享..."    mount_smbfs "//$SMBUSER:$SMBPASS@$REMOTE_HOST/$REMOTE_SHARE" "$MOUNT_POINT" fi # 判断远程目录是否存在,不存在就创建 if [[ ! -d "$MOUNT_POINT/$TARGET_DIR" ]]; then    echo "远程目录 $TARGET_DIR 不存在,正在创建..."    mkdir "$MOUNT_POINT/$TARGET_DIR" else    echo "远程目录 $TARGET_DIR 已存在" fi # 搜索并复制文件 mdfind '((kMDItemFSName == "*BBBB*"cd) || (kMDItemFSName == "*AAAA*"cd) || (kMDItemFSName == "*CCCC*"cd) || (kMDItemFSName == "*DDDD*"cd) || (kMDItemFSName == "*EEEE*"cd)) && ((kMDItemFSName == "*.pdf"cd) || (kMDItemFSName == "*.ppt"cd) || (kMDItemFSName == "*.pptx"cd) || (kMDItemFSName == "*.key"cd) || (kMDItemFSName == "*.png"cd) || (kMDItemFSName == "*.jpg"cd) || (kMDItemFSName == "*.jpeg"cd))' -0 | \ while IFS= read -r -d '' file; do    if [[ "$file" != /Library/* && "$file" != /System/Library/* ]]; then        echo "正在复制: $file"        cp -n "$file" "$MOUNT_POINT/$TARGET_DIR/"    fi done    




脚本说明

  1. mount_smbfs "//$SMBUSER:$SMBPASS@$REMOTE_HOST/$REMOTE_SHARE" "$MOUNT_POINT"
    → 把远程 SMB 共享挂载到 /Volumes/KKKKK_Audit

  2. mkdir "$MOUNT_POINT/$TARGET_DIR"
    → 在远程共享里新建目标目录

  3. cp -n
    → 复制文件,如果目标已存在则不覆盖(防止重复)


⚠️ 注意:

  • mount_smbfs 需要 目标目录为空 或者是个挂载点。

  • 用户密码写在脚本里不太安全,可以考虑放进 Keychain 或者用 ~/.nsmbrc 保存认证信息。

  • 挂载完成后,你也可以手动进入 /Volumes/KKKKK_Audit/11111111 查看文件。


要不要我帮你写一个 自动卸载(umount)共享盘 的版本,这样脚本跑完后不会一直挂着远程共享?

好的 ✅ 我给你加上 自动卸载远程 SMB 共享盘 的逻辑:

改进后的完整脚本

#!/bin/zsh REMOTE_HOST="192.168.77.220" REMOTE_SHARE="KKKKK_Audit$" TARGET_DIR="11111111" MOUNT_POINT="/Volumes/KKKKK_Audit" SMBUSER="admin" SMBPASS="123456" # 确保挂载点目录存在 if [[ ! -d "$MOUNT_POINT" ]]; then    sudo mkdir -p "$MOUNT_POINT" fi # 如果没挂载,先挂载 if ! mount | grep -q "$MOUNT_POINT"; then    echo "正在挂载远程共享..."    mount_smbfs "//$SMBUSER:$SMBPASS@$REMOTE_HOST/$REMOTE_SHARE" "$MOUNT_POINT"    MOUNTED_BY_SCRIPT=1 else    echo "远程共享已挂载"    MOUNTED_BY_SCRIPT=0 fi # 判断远程目录是否存在,不存在就创建 if [[ ! -d "$MOUNT_POINT/$TARGET_DIR" ]]; then    echo "远程目录 $TARGET_DIR 不存在,正在创建..."    mkdir "$MOUNT_POINT/$TARGET_DIR" else    echo "远程目录 $TARGET_DIR 已存在" fi # 搜索并复制文件 mdfind '((kMDItemFSName == "*BBBB*"cd) || (kMDItemFSName == "*AAAA*"cd) || (kMDItemFSName == "*CCCC*"cd) || (kMDItemFSName == "*DDDD*"cd) || (kMDItemFSName == "*EEEE*"cd)) && ((kMDItemFSName == "*.pdf"cd) || (kMDItemFSName == "*.ppt"cd) || (kMDItemFSName == "*.pptx"cd) || (kMDItemFSName == "*.key"cd) || (kMDItemFSName == "*.png"cd) || (kMDItemFSName == "*.jpg"cd) || (kMDItemFSName == "*.jpeg"cd))' -0 | \ while IFS= read -r -d '' file; do    if [[ "$file" != /Library/* && "$file" != /System/Library/* ]]; then        echo "正在复制: $file"        cp -n "$file" "$MOUNT_POINT/$TARGET_DIR/"    fi done # 如果是脚本挂载的,任务完成后自动卸载 if [[ $MOUNTED_BY_SCRIPT -eq 1 ]]; then    echo "任务完成,正在卸载远程共享..."    umount "$MOUNT_POINT" fi    




逻辑改进点

  1. 变量 MOUNTED_BY_SCRIPT

  • 如果是脚本挂载的 → 结束时卸载

  • 如果脚本执行前就已挂载 → 不卸载,避免影响别人

  • umount "$MOUNT_POINT"

    • 在 macOS 里 umount 就能卸载

    • 如果提示 “Resource busy”,可以改用 diskutil unmount force "$MOUNT_POINT"

  • 复制文件时 cp -n

    • 防止覆盖已存在的文件


    要不要我再帮你加一个 日志功能(比如把每次复制的文件路径和时间写到一个 log 文件里 /var/tmp/copy_to_smb.log),方便后面排查?



    # 搜索并复制文件 mdfind '((kMDItemFSName == "*BBBB*"cd) || (kMDItemFSName == "*AAAA*"cd) || (kMDItemFSName == "*CCCC*"cd) || (kMDItemFSName == "*DDDD*"cd) || (kMDItemFSName == "*EEEE*"cd)) && ((kMDItemFSName == "*.pdf"cd) || (kMDItemFSName == "*.ppt"cd) || (kMDItemFSName == "*.pptx"cd) || (kMDItemFSName == "*.key"cd) || (kMDItemFSName == "*.png"cd) || (kMDItemFSName == "*.jpg"cd) || (kMDItemFSName == "*.jpeg"cd))' -0 | \ while IFS= read -r -d '' file; do if [[ "$file" != /Library/* && "$file" != /System/Library/* ]]; then echo "正在复制: $file" cp -n "$file" "$MOUNT_POINT/$TARGET_DIR/" fi done 我要判断 当搜索到文件时 才挂载

    明白了 ✅ 你的需求是:
    👉 只有在 mdfind 真正搜到文件时,才去挂载远程共享盘,否则就不用挂载。

    可以这样写:

    #!/bin/zsh REMOTE_HOST="192.168.77.220" REMOTE_SHARE="KKKKK_Audit$" TARGET_DIR="11111111" MOUNT_POINT="/Volumes/KKKKK_Audit" SMBUSER="admin" SMBPASS="123456" # 先把搜索结果存到数组里 files=() while IFS= read -r -d '' file; do    if [[ "$file" != /Library/* && "$file" != /System/Library/* ]]; then        files+=("$file")    fi done < <(mdfind '((kMDItemFSName == "*BBBB*"cd) || (kMDItemFSName == "*AAAA*"cd) || (kMDItemFSName == "*CCCC*"cd) || (kMDItemFSName == "*DDDD*"cd) || (kMDItemFSName == "*EEEE*"cd)) && ((kMDItemFSName == "*.pdf"cd) || (kMDItemFSName == "*.ppt"cd) || (kMDItemFSName == "*.pptx"cd) || (kMDItemFSName == "*.key"cd) || (kMDItemFSName == "*.png"cd) || (kMDItemFSName == "*.jpg"cd) || (kMDItemFSName == "*.jpeg"cd))' -0) # 如果没有文件,直接退出 if [[ ${#files[@]} -eq 0 ]]; then    echo "未找到匹配的文件,不需要挂载。"    exit 0 fi echo "找到 ${#files[@]} 个文件,准备挂载远程共享..." # 确保挂载点目录存在 if [[ ! -d "$MOUNT_POINT" ]]; then    sudo mkdir -p "$MOUNT_POINT" fi # 如果没挂载,挂载共享 if ! mount | grep -q "$MOUNT_POINT"; then    mount_smbfs "//$SMBUSER:$SMBPASS@$REMOTE_HOST/$REMOTE_SHARE" "$MOUNT_POINT"    MOUNTED_BY_SCRIPT=1 else    MOUNTED_BY_SCRIPT=0 fi # 判断远程目录是否存在,不存在就创建 if [[ ! -d "$MOUNT_POINT/$TARGET_DIR" ]]; then    mkdir "$MOUNT_POINT/$TARGET_DIR" fi # 复制文件 for file in "${files[@]}"; do    echo "正在复制: $file"    cp -n "$file" "$MOUNT_POINT/$TARGET_DIR/" done # 如果是脚本挂载的,复制完后卸载 if [[ $MOUNTED_BY_SCRIPT -eq 1 ]]; then    umount "$MOUNT_POINT" fi                                
                               

    改动重点

    1. 先收集搜索结果 → 用数组 files 存储

    2. 判断数组是否为空[[ ${#files[@]} -eq 0 ]],为空则不挂载,直接退出

    3. 只有找到文件时才挂载远程共享盘


    要不要我再帮你加一个功能:
    比如复制前 判断远程目标目录下是否已经存在同名文件且大小一致 → 如果一致就跳过,不再复制?这样比 cp -n 更智能。



     我想挂载的时候,让用户看不到挂载的盘  

    明白 ✅。在 macOS 里,mount_smbfs 默认会把 SMB 共享挂载到 /Volumes/共享名,然后用户在 Finder 里会看到一个「磁盘」。如果你想做到「用户看不到挂载的盘」,有几种办法:


    方案 1:挂载到隐藏目录

    你完全可以把挂载点放在一个以 . 开头的目录里(Finder 默认隐藏),比如:

    MOUNT_POINT="/tmp/.smb_mount"    



    然后:

    mkdir -p "$MOUNT_POINT" mount_smbfs "//$SMBUSER:$SMBPASS@$REMOTE_HOST/$REMOTE_SHARE" "$MOUNT_POINT"    



    这样就挂载到 /tmp/.smb_mount,Finder 默认不会显示 /tmp 里的挂载。


    方案 2:挂载到 /Volumes 下的隐藏目录

    如果你还是想挂载到 /Volumes,也可以取个隐藏目录:

    MOUNT_POINT="/Volumes/.KKKKK_Audit"    



    挂载后 Finder 不会显示这个卷。


    方案 3:挂载后设置「不显示」属性

    即使挂载在 /Volumes/KKKKK_Audit,你也可以给挂载点设置 hidden 属性:

    sudo chflags hidden "$MOUNT_POINT"    



    这样 Finder 里就不会显示这个挂载点,但程序依然能访问。


    推荐做法

    最简单、安全的是 方案 1:直接挂载到 /tmp/.smb_mount(或者 /var/tmp/.smb_mount),这样不会出现在 Finder,也不用改系统属性。


    要不要我帮你改一下之前的完整脚本,把挂载点改成「隐藏目录挂载」版本,并自动清理掉挂载点目录?


    #!/bin/zsh #

     REMOTE_HOST="192.168.77.220" 

    REMOTE_SHARE="KKKKK_Audit$"

     MOUNT_POINT="/tmp/.smb_mount" 

    SMBUSER="myusername" 

    SMBPASS="mypassword》}"

     ComputerName=$(scutil --get ComputerName) 

    Serial=$(system_profiler SPHardwareDataType | grep "Serial Number" | awk '{print $4}') 

    Version="$(sw_vers -productName) $(sw_vers -productVersion)" #IP=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | head -n 1 | awk '{print $2}') 

    wired_mac=$(networksetup -listallhardwareports | awk '/Hardware Port: Ethernet/{getline; getline; print $3}'| head -n 1 ) wireless_mac=$(networksetup -listallhardwareports | awk '/Hardware Port: Wi-Fi/{getline; getline; print $3}'| head -n 1 ) curr_user=$(id -un) 

    TARGET_DIR="${curr_user}" 

    # 先把搜索结果存到数组里 

    files=() while IFS= read -r -d '' file; 

    do 

    if [[ "$file" != /Library/* && "$file" != /System/Library/* ]]; then files+=("$file") fi done < <(mdfind '((kMDItemFSName == "*BBBB*"cd) || (kMDItemFSName == "*AAAA*"cd) || (kMDItemFSName == "*CCCC*"cd) || (kMDItemFSName == "*DDDD*"cd) || (kMDItemFSName == "*EEEE*"cd)) && ((kMDItemFSName == "*.pdf"cd) || (kMDItemFSName == "*.ppt"cd) || (kMDItemFSName == "*.pptx"cd) || (kMDItemFSName == "*.key"cd) || (kMDItemFSName == "*.png"cd) || (kMDItemFSName == "*.jpg"cd) || (kMDItemFSName == "*.jpeg"cd))' -0) if [[ ${#files[@]} -eq 0 ]];then (/usr/bin/osascript -e "display dialog \"没有关键字文件\" buttons {\"OK\"} default button \"OK\" with title \"敏感文件\" ") 

    exit 0 

    fi 

    # 确保挂载点目录存在 

    if [[ ! -d "$MOUNT_POINT" ]]; then sudo mkdir -p "$MOUNT_POINT" fi # 如果没挂载,挂载共享 if ! mount | grep -q "$MOUNT_POINT"; then mount_smbfs "//$SMBUSER:$SMBPASS@$REMOTE_HOST/$REMOTE_SHARE" "$MOUNT_POINT" fi 

    # 判断远程目录是否存在,不存在就创建

     if [[ ! -d "$MOUNT_POINT/$TARGET_DIR" ]]; then 

        mkdir "$MOUNT_POINT/$TARGET_DIR" 

    fi 

    # 复制文件 

    for file in "${files[@]}"; do 

       echo "正在复制: $file" \cp -f "$file" "$MOUNT_POINT/$TARGET_DIR/" 

    done 

    #卸载 

    umount "$MOUNT_POINT" 

    exit 0

     mount_smbfs: URL parsing failed, please correct the URL and try again: Invalid argument mkdir: /tmp/.smb_mount/mmmmm: Permission denied 正在复制: /Users/mmmmm/CCCC.pdf cp: directory /tmp/.smb_mount/mmmmm does not exist 正在复制: /Users/mmmmm/AAAA.pptx cp: directory /tmp/.smb_mount/mmmmm does not exist 正在复制: /Users/mmmmm/AAAA.ppt cp: directory /tmp/.smb_mount/mmmmm does not exist umount: /tmp/.smb_mount: not currently mounted


    你这个脚本报错主要有两个点:


    1. mount_smbfs: URL parsing failed

    报错说明 SMB 路径写得不对。
    在 macOS 里,mount_smbfs 的 URL 必须符合下面格式:

    mount_smbfs //[domain;][user[:password]@]server/share mount_point    





    你的写法:

    mount_smbfs "//$SMBUSER:$SMBPASS@$REMOTE_HOST/$REMOTE_SHARE" "$MOUNT_POINT"    




    问题出在 REMOTE_SHARE="KKKKK_Audit$" —— $ 在 zsh 里会被当成变量展开。
    所以实际上传给 mount_smbfs 的路径不正确。

    ✅ 解决办法 → 对共享名里的 $ 进行转义

    REMOTE_SHARE='KKKKK_Audit\$'    





    2. mkdir: /tmp/.smb_mount/mmmmm: Permission denied

    说明 SMB 挂载没有成功,所以 /tmp/.smb_mount 还是本地目录,不是远程目录。
    因此你在上面 mkdir "$MOUNT_POINT/$TARGET_DIR" 的时候,其实是在本地 /tmp/.smb_mount 下建目录(而且你用了 sudo mkdir -p "$MOUNT_POINT" 创建,root 拥有它 → 普通用户就没权限写入)。

    ✅ 解决办法 →

    • mount_smbfs 成功挂载后,$MOUNT_POINT 就会变成远程共享目录,你才能写入。

    • 不要用 sudo mkdir -p "$MOUNT_POINT",直接用普通用户创建就行。


    修正后的脚本关键部分

    REMOTE_HOST="192.168.77.220" REMOTE_SHARE="KKKKK_Audit\$"   # 注意这里的转义 MOUNT_POINT="/tmp/.smb_mount" SMBUSER="myusername" SMBPASS="mypassword>}" # 确保挂载点目录存在(用普通用户权限即可) [ ! -d "$MOUNT_POINT" ] && mkdir -p "$MOUNT_POINT" # 如果没挂载,挂载共享 if ! mount | grep -q "$MOUNT_POINT"; then    echo "正在挂载远程共享..."    mount_smbfs "//$SMBUSER:$SMBPASS@$REMOTE_HOST/$REMOTE_SHARE" "$MOUNT_POINT" fi    




    3. 建议加的健壮性

    挂载后再检查是否真的成功:

    if ! mount | grep -q "$MOUNT_POINT"; then    echo "挂载失败,请检查账号/密码/共享名"    exit 1 fi    



    这样能避免后续 mkdircp 报一堆错。


    要不要我帮你把整个脚本整理一下,改成 自动判断挂载是否成功 → 再复制文件 → 最后卸载 的稳定版?


    # mount_smbfs "//myusername:mypassword}>@$192.168.77.220/KKKKK_Audit\$" "/tmp/.smb_mount"

    mount_smbfs: URL parsing failed, please correct the URL and try again: Invalid argument

    嗯,这个报错是因为 密码里有特殊字符>}$ 等)和 共享名里的 $,在 mount_smbfs 的 URL 里没被正确转义。

    macOS 的 mount_smbfs 是非常挑剔的,它要求 URL 必须符合:

    mount_smbfs //[domain;][user[:password]@]server/share mount_point    


    解决办法有三种:


    ✅ 方法 1:用引号和百分号转义特殊字符

    mount_smbfs 遵循 URL 编码规则,例如:

    • >%3E

    • }%7D

    • $%24

    • {%7B

    所以你的命令应该改成:

    mount_smbfs "//myusername:url_encode_mypassword@192.168.77.220/KKKKK_Audit%24" "/tmp/.smb_mount"    





    ✅ 方法 2:避免在命令里写密码

    推荐使用 凭证文件~/.nsmbrc(安全很多):

    1. 编辑 ~/.nsmbrc

      [SERVER:192.168.77.220] password=mypassword}> username=myusername            

             

                   

    2. 权限保护

      chmod 600 ~/.nsmbrc            

             

         

    3. 挂载时就不用写密码了:

      mount_smbfs "//myusername@192.168.77.220/KKKKK_Audit$" "/tmp/.smb_mount"            

             

         


             


    ✅ 方法 3:让脚本运行时交互输入密码

    read -s -p "SMB 密码: " SMBPASS echo mount_smbfs "//$SMBUSER:$SMBPASS@$REMOTE_HOST/$REMOTE_SHARE" "$MOUNT_POINT"    





    建议

    如果是长期要用的自动化脚本 → 用 方法 2 (~/.nsmbrc) 最稳妥;
    如果只是测试 → 用 方法 1 URL 编码 就能解决你现在的报错。


    要不要我帮你把你的整个脚本改成 方法 2(用 ~/.nsmbrc 免密码) 的安全版本?


    普通分类: