6Tom 发表于 2022-7-29 23:09

【Batch】给微软自家命令行程序robocopy做了个常用代码库

本帖最后由 6Tom 于 2022-9-16 21:55 编辑

PS: 2022.9.16发出22代更新,因为篇幅比较长,所以放在9楼并置顶了,需要新版请去9楼查看
新人首帖,多多包涵,手工码字,如果有啥不正确的,请立即指出{:1_924:}

从vista(包括)之后,windows其实就内置了一款命令行拷贝程序robocopy,但是一直没啥人知道,火不起来,一个是本身是命令行程序,并没有GUI,二个是微软也没特意给科普过。

实际上这是一款非常强大的拷贝工具,举两个例子:

1.当你试图复制,尤其是跨盘符复制【大量零碎小文件】的时候,就会发现不管你是多么牛逼的机械硬盘,还是固态硬盘也好,复制速度根本上不去{:1_907:},因为在资源管理器中,复制是单线程的,意味着文件无论大小,都要站成单人一队,挨个被复制,这就导致了读写速度根本没有用武之地{:1_901:}。由此,网络上诞生了许多多线程复制工具,最有名的应该就是fastcopy了,低版本的还是开源的。(不过fastcopy主打的是缓冲区,多线程算是个附带品,在这儿不做讨论,现在重点关注的是大量零碎小文件的拷贝。)
https://attach.52pojie.cn//forum/202207/29/174541zc72ajd3t2tlttyj.jpg?l(图:fastcopy)

【但是这种涉及硬盘直接读写的工具,还是应该谨慎、再谨慎。】网上有一种说法是,fastcopy伤硬盘,还有人现身说法,用自己用坏硬盘的经历佐证。无论真假,总之是,没有必要去冒的风险,就不去冒{:1_911:},这不微软自家就有工具吗,安全性首先就有保证。


[*]robocopy最高支持128线程复制,也就是说可以站128条单人队,那这流量就放开了,速度自然也上来了:keai。




2.当你想依据已有目录树,【只镜像目录树】,却不想要里面的文件的时候。如果按照老式做法,要不就是先复制,然后用del /f /s /q跨嵌套删文件;要不就是写一个复杂一点的批处理,用for循环、dir遍历、md创建新文件夹:rggrg;要不就是借助其他第三方软件。
https://attach.52pojie.cn//forum/202207/29/180330bwq3l29wjz2iqluz.jpg?l(图:目录树)


[*]现在微软给你途径了,robocopy支持按时间筛选文件,最早支持到1900年1月1日,只要不是人为恶搞情况下,谁电脑上都不可能有早于这个日期的文件,借此便可以实现【只镜像目录树】的目的。




【下面是今天的主角出场】命令行工具毕竟有其局限性,需要敲代码,这就是我想构建这个常用代码库的原因,需要用到的时候,直接调用就可以了,欢迎大家一同来完善这个常用库,共同提高大家的工作效率,并保护好硬盘不受伤:lol

先展示下效果,复制文件,做gif就不展示复制太多文件的效果了,就是演示一下使用方法,因为这么一点点文件完全不需要robocopy,也无法展示robocopy的实力
https://attach.52pojie.cn//forum/202207/29/211607vm0z0ikwftz08tj8.gif?l这是通过拖动复制文件夹的效果

https://attach.52pojie.cn//forum/202207/29/211632h47dfb2as2igqz2u.gif?l这是如果拷贝失败,会自动询问管理员权限(如果你设置了关闭UAC弹窗则会自动以管理员身份重试),选择确定后可以管理员身份重试,如果成功了就是图示这样,如果还是失败就会提示失败并退出,这种情况应该不多见,这种复杂的就需要自己找原因了

下面上代码,我习惯全英写,所以提供下载的文件里是没有中文注释的,网页上为了方便大家阅读,写了中文注释,但是吾爱这儿不支持Batch,只能选了个vb,起码语句之间颜色能有点区分,感兴趣并且有条件的可以复制到vscode里面去看,很清晰直观

先上库,这个下文中中文注释会称为“库”,文件名lib.bat
然后解释一下啥叫caller,下文就不再解释了,caller就比如下文要出现的文件“碎文件拷贝.bat”,这个文件里会有命令call "%lib_%" :prelude,就是“碎文件拷贝.bat”这个caller在调用库里的:prelude模块
然后需要说一下,这个是只针对文件夹拷贝,所以不要拖拽文件哈,因为同时拖拽多个文件到cmd窗口是无效的,所以不考虑非文件夹拷贝,没有意义

goto %1
:out
exit /b
rem ----------------------------------------
:prelude
rem 这是前序引导,检查caller里有没有用户自定义的robocopy命令,如果没有,就检查库里有没有从caller传递过来的标签
rem check whether there is a custom command, check whether the way is in the lib
rem 下一行是展示必须从caller获得的参数
rem need from caller: "%self_%""%way_%"
rem 下一行是展示这个模块会输出什么
rem output: "%tmp_%""%com_%"
@echo off
rem 下一行是切换控制台到utf-8,因为ansi的结果是中文的,没法查找里面想要的字符串,所以必须使用utf-8
chcp 65001
cls
set "tmp_=temp.bat"
::
rem 这是检查用户自定义的robocopy命令,如果有就做个延迟,防止路径变量丢失,并退出模块,
rem check command
if defined com_ (
    for /f "tokens=2 delims==" %%a in ('findstr /i /c:"com_=" "%self_%"') do (
      set "com_=%%a"
      call set "com_=%%com_:~0,-1%%"
    )
    goto :out
)
::
rem 检查库里有没有从caller传递过来的标签,没有就通知“库里没有这种方法”,并退出
rem check the way
for /f "tokens=1 delims=:" %%a in ('findstr /n /i /b /c:"%way_%" %0') do set /a "line_way=%%a+1"
if "%line_way%"=="" (
    echo/There is no such way in the lib, about to exit.
    pause
    exit
)
for /f "tokens=1,* delims=:" %%a in ('findstr /n ".*" %0') do if %%a equ %line_way% set "com_=%%b"
goto :out
::
rem ----------------------------------------
:get_path
rem 下面是实现把文件夹拖拽到cmd窗口的模块
rem get source and target paths
rem 下一行是展示这个模块会输出什么
rem output: "%path_from%""%path_to%"
::
rem 下面是检查变量%path_from%是否已经被定义,有就不再通过拖拽定义了
rem check whether the source path needs to be gotten
if defined path_from (
    if exist "%path_from%" goto :check_target
)
::
:source
rem 下面是拖拽源文件夹的代码,以及如果输入了错值、不存在的文件夹,自动提醒重新输入
rem get source path
set /p "path_from=From? "
if not defined path_from set "path_from= "
rem 这是我做的一个容错,把输入的如果有引号去掉,这样即使引号不是成对的,缺失了一个,也不会出现问题
set "path_from=%path_from:"=%"
if not exist "%path_from%" (
    echo/
    echo/This path does not exist, please re-enter.
    echo/
    echo/
    goto :source
)
dir "%path_from%" | findstr /i "<DIR>">nul
if %errorlevel% neq 0 (
    echo/
    echo/This is not a folder, please choose a folder.
    echo/
    echo/
    goto :source
)
rem 这是我做的一个容错,因为也可能是手敲的,如果最后一位习惯性的敲了斜杠“\”,这里会自动去掉,否则robocopy会失败
if "%path_from:~-1%"=="\" set "path_from=%path_from:~0,-1%"
::
:check_target
rem 下面是检查变量%path_to%是否已经被定义,有的话检查路径是否存在,存在就不再通过拖拽定义了,否则还是拖拽定义
rem check whether the target path needs to be gotten
if defined path_to goto :skip_set_target
::
:target
rem 下面是拖拽目标文件夹的代码,以及如果输入了空值,自动提醒重新输入,注意目标文件夹是可以本来不存在的,后续可以自动创建
rem get target path
echo/
set /p "path_to=To? "
if not defined path_to set "path_to= "
::
:skip_set_target
set "path_to=%path_to:"=%"
if not defined path_to set "path_to= "
set "path_tmp=%path_to: =%"
if "%path_tmp%"=="" (
    echo/
    echo/Please do not enter a null value.
    echo/
    echo/
    goto :target
)
rem 这是判断输入的驱动器是否存在,不存在则自动提醒重输
rem check whether this drive exists
if "%path_to:~1,1%"==":" (
    if not exist "%path_to:~0,2%\" (
      echo/
      echo/This drive does not exist, please re-enter.
      echo/
      goto :target
    )
)
rem 这是我做的一个容错,因为也可能是手敲的,如果最后一位习惯性的敲了斜杠“\”,这里会自动去掉,否则robocopy会失败
if "%path_to:~-1%"=="\" set "path_to=%path_to:~0,-1%"
goto :out
::
rem ----------------------------------------
:get_time
rem 下面是记录时间点模块,如果需要统计工作时长,计时开始时调用一次,计时结束后调用一次即可
rem get start or end time
rem 下一行是展示从caller获得的可选参数,非必须
rem optional need from caller: "%check_time%"
rem 下一行是展示这个模块会输出什么
rem output: "%time_*%""%min_*%""%sec_*%""%ms_*%"
::
rem 这是检查用户是否需要计时,如果变量%check_time%的值为n,就不计时了
rem check whether timing is required
if "%check_time%"=="n" goto :out
::
rem get time
set /a "num_+=1"
rem 下一行是为了保证严谨,因为不一定所有电脑都是24小时制,有人用的是12小时制,为了使两种都成立
if "%time:~0,1%"==" " (set "time_now=0%time:~1%") else (set "time_now=%time%")
set "time_%num_%=%time_now%"
if "%time_now:~3,1%"=="0" (set "min_%num_%=%time_now:~4,1%") else (set "min_%num_%=%time_now:~3,2%")
if "%time_now:~6,1%"=="0" (set "sec_%num_%=%time_now:~7,1%") else (set "sec_%num_%=%time_now:~6,2%")
if "%time_now:~9,1%"=="0" (set "ms_%num_%=%time_now:~10,1%") else (set "ms_%num_%=%time_now:~9,2%")
goto :out
::
rem ----------------------------------------
:duration
rem 下面是计算时长的模块
rem calculate duration
rem 下一行是展示从caller获得的可选参数,非必须
rem optional need from caller: "%check_time%"
rem 下一行是展示,如果想要计算时长,那么必须要计时开始、计时结束时调用记录时间点模块
rem need from lib: ":get_time"x2
rem 下一行是展示这个模块会输出什么
rem output: "%min_%""%sec_%""%ms_%"
::
rem 这是检查用户是否需要计时,如果变量%check_time%的值为n,就不计时了
rem check whether timing is required
if "%check_time%"=="n" goto :out
::
rem get time
if %ms_2% lss %ms_1% set /a "ms_2+=100" & set /a "sec_2-=1"
if %sec_2% lss %sec_1% set /a "sec_2+=60" & set /a "min_2-=1"
set /a "min_=%min_2%-%min_1%"
set /a "sec_=%sec_2%-%sec_1%"
set /a "ms_=%ms_2%-%ms_1%"
goto :out
::
rem ----------------------------------------
:results
rem 下面是结果展示模块
rem show results
rem 下一行是展示从caller获得的可选参数,非必须
rem optional need from caller: "%check_time%"
rem 下一行是展示,要想使用此模块,必须先使用过什么模块
rem need from lib: ":perform"":duration"
::
rem 这是检查用户是否需要计时,如果变量%check_time%的值为n,就输出简易结果,只“完成”
rem check whether timing is required
if "%check_time%"=="n" (
    echo/
    echo/Done.
    goto :out
)
::
rem 下面是输出复杂结果
rem results
rem 有的时候明明完成了拷贝,但是却没有速度结果,这个是robocopy本来就这样,不知道为啥,这锅我不背,没有速度就显示为N/A
if "%speed_%"=="" set "speed_=N/A"
echo/
echo/--------------------------------------------------
echo/ Started :%time_1%
echo/   Ended :%time_2%
echo/Duration :%min_% min %sec_% s %ms_% ms
echo/   Speed :%speed_% MB/s
echo/Done.
goto :out
::
rem ----------------------------------------
:perform
rem 这个是拷贝的执行模块
rem perform the copy operation
rem 下一行是展示必须从caller获得的参数
rem need from caller: "%self_%"
rem 下一行是展示从caller获得的可选参数,非必须
rem optional need from caller: "%com_%""%path_from%""%path_to%"
rem 下一行是展示,要想使用此模块,必须先使用过什么模块
rem need from lib: ":prelude"":get_path"
rem 下一行是展示这个模块会输出什么
rem output: "%err_code%""%speed_%"
echo/
echo/Copying. . . Wait patiently and do not click here
rem 下面用for循环和findstr是为了找到是否发生错误,以及“速度”,并且这样自动关闭了robocopy的回显,使运行变快,可以试一下robocopy如果开回显,效率会大打折扣,我试验了一批文件,不回显是18秒,开回显用了1分多钟
for /f "tokens=3 delims=. " %%a in ('%com_% ^| findstr /c:" ERROR " /c:" MegaBytes/min."') do (
    if "%%a"=="ERROR" (set /a "err_code+=1" & goto :admin) else (set /a "speed_=%%a/60")
)
goto :out
::
:admin
rem 下面是如果发生错误,以管理员身份重试的代码
rem 下面是检测试验次数,如果第二次还是失败,那就是彻底失败,不是权限的问题,退出自行找原因
rem check error code
if %err_code% equ 2 (
    echo/
    echo/Failed, about to exit
    pause
    del /f /q "%tmp_%"
)
::
rem 下面是建立临时文件的代码,以便以管理员身份重新运行
rem code of the temp bat
rem 下面是找行号的代码
for /f "tokens=1 delims=:" %%a in ('findstr /n /i /b /c:":code_begin" %0') do set "line_code_begin=%%a"
for /f "tokens=1 delims=:" %%a in ('findstr /n /i /b /c:":code_end" %0') do set "line_code_end=%%a"
::
rem 下面是输出到临时文件的代码
(for /f "tokens=1,* delims=:" %%a in ('findstr /n ".*" %0') do if %%a gtr %line_code_begin% if %%a lss %line_code_end% echo/%%b
echo/set "path_from=%path_from%"
echo/set "path_to=%path_to%"
more "%self_%"
echo/del /f /q "%tmp_%"
)>"%tmp_%"
::
rem 这是启动临时文件并退出当前窗口
start "" "%tmp_%"
exit
::
rem 下面两个标签之间是要写进临时文件的代码,注意chcp 65001是为了防止如果路径含有中文会乱码
:code_begin
%1 start "" mshta vbscript:createobject("shell.application").shellexecute("""%~0""","::",,"runas",1)(window.close) & exit
chcp 65001
set "err_code=1"
:code_end
::
::
rem ----------------------------------------
rem 下面是robocopy的命令模块
rem the following is the robocopy command area
rem 下一行是展示从caller获得的可选参数,非必须
rem optional need from caller: "%path_from%""%path_to%"
rem 下一行是展示,必须使用过什么模块
rem need from lib: ":get_path"
rem 如果要添加robocopy命令,写法是先写标签,然后紧挨着下一行写命令,不要隔行
rem writing method: one line of the label, and the next line writes the robocopy command
rem ----------------------------------------
::
rem 这是碎文件拷贝的命令
:copy_normal
robocopy "%path_from%" "%path_to%" /s /mt:128 /nodcopy /r:0
::
rem 这是拷贝目录结构的命令
:copy_tree
robocopy "%path_from%" "%path_to%" /e /mt:128 /nodcopy /r:0 /minage:19000101


目前库里robocopy命令只有两条,是我根据自己需要写的,大家可以根据需要自行补充,有啥好的命令也可以分享出来给大家一块看看
下面示范三个caller,第一个,文件名“碎文件拷贝.bat”,需要和lib.bat放在同一个文件夹内
rem editable area
rem 这是设置方法,也就是库里的robocopy命令,设置变量%way_%的值为标签即可
set "way_=:copy_normal"
::
rem ----------------------------------------
rem 下面是傻瓜化模块,通常情况下不需要更改
rem areas that do not normally need editing
cd /d "%~dp0"
set "lib_=lib.bat"
rem 下一行是检查库存不存在
if not exist "%lib_%" exit
rem 下一行是定义变量%self_%,也就是caller自己,用于传递给库
set "self_=%~0"
::
rem 下面是前序,先做方法检查,看库里有没有
rem check whether the way is in the lib
call "%lib_%" :prelude
::
rem 下面是拖拽文件夹
rem get path
call "%lib_%" :get_path
::
rem 下面是开始计时
rem get start time
call "%lib_%" :get_time
::
rem 下面是执行拷贝
rem copy
call "%lib_%" :perform
::
rem 下面是结束计时
rem get end time
call "%lib_%" :get_time
::
rem 下面是计算时长
rem calculate duration
call "%lib_%" :duration
::
rem 下面是展示结果
rem show results
call "%lib_%" :results
pause

示例第二个caller,文件名“拷贝目录结构.bat”

rem editable area
set "way_=:copy_tree"
rem 只要不是过于复杂,比如拷贝c:\windows目录结构,或者拷贝整个盘符的结构,拷贝结构都是非常快的,所以就不计时了,只需要加一行这个,其他的完全可以不动
set "check_time=n"
::
rem ----------------------------------------
rem areas that do not normally need editing
cd /d "%~dp0"
set "lib_=lib.bat"
if not exist "%lib_%" exit
set "self_=%~0"
::
rem check whether the way is in the lib
call "%lib_%" :prelude
::
rem get path
call "%lib_%" :get_path
::
rem 不计时可以不调用这个模块,但是懒的话不删也没有问题
rem get start time
call "%lib_%" :get_time
::
rem copy
call "%lib_%" :perform
::
rem 本为结束计时,同理
rem get end time
call "%lib_%" :get_time
::
rem 本为计算时长,同理
rem calculate duration
call "%lib_%" :duration
::
rem 不计时将自动展示简易结果,去掉的话就是没有结果
rem show results
call "%lib_%" :results
pause

示例第三个caller,自定义robocopy命令

rem editable area
rem 设置自定义命令,变量名为%com_%,在等号后面写robocopy命令,注意写完最后一定要加个引号,这里以8线程、空文件夹也拷贝为例
set "com_=robocopy "%path_from%" "%path_to%" /e /mt:8 /nodcopy /r:0"
::
rem ----------------------------------------
rem 别的不用改
rem areas that do not normally need editing
cd /d "%~dp0"
set "lib_=lib.bat"
if not exist "%lib_%" exit
set "self_=%~0"
::
rem check whether the way is in the lib
call "%lib_%" :prelude
::
rem get path
call "%lib_%" :get_path
::
rem get start time
call "%lib_%" :get_time
::
rem copy
call "%lib_%" :perform
::
rem get end time
call "%lib_%" :get_time
::
rem calculate duration
call "%lib_%" :duration
::
rem show results
call "%lib_%" :results
pause

示例第四个caller,自定义源路径,不再拖拽源路径,只需要再拖拽目标路径

rem editable area
set "way_=:copy_normal"
rem 注意如果自定义路径有中文,必须先写chcp 65001,把控制台调到utf-8,否则后续会乱码,找不到路径
chcp 65001
rem 下面自定义源路径,后续就不需要再拖拽了,只需要再推拽目标路径
set "path_from=d:\desktop\试验"
::
rem ----------------------------------------
rem 别的不用改
rem areas that do not normally need editing
cd /d "%~dp0"
set "lib_=lib.bat"
if not exist "%lib_%" exit
set "self_=%~0"
::
rem check whether the way is in the lib
call "%lib_%" :prelude
::
rem get path
call "%lib_%" :get_path
::
rem get start time
call "%lib_%" :get_time
::
rem copy
call "%lib_%" :perform
::
rem get end time
call "%lib_%" :get_time
::
rem calculate duration
call "%lib_%" :duration
::
rem show results
call "%lib_%" :results
pause

行了,先这样,要是有人用的话,有啥bug就在这跟我说吧,等我有空了看看,这虽然是第一次公开发出来,但是在我手头这已经是第8代了,应该没啥很大的问题,欢迎大家来一起完善这个库,微软自家好工具,用起来{:1_927:}


刚发布就做了个小更新,9代加了个判断目标文件夹所在的驱动器是否存在,不存在则需要重输,9代链接:https://pan.baidu.com/s/15GTgZNyreDNxx6FnSV2eLQ?pwd=6kfa

6Tom 发表于 2022-9-16 21:44

没人反馈,自己根据自己使用习惯,自己改进,发出第22代,不得不说现在确实比之前好用多了
因为加了一些功能,代码篇幅大增,所以不在网页上长篇大论了,直接打包发,更新内容如下:

功能性更新:
1.目标默认桌面:当输入目标路径时,如果不写盘符,只写文件夹名称,则自动在桌面上建立这个名称的文件夹,复制到那里


2.星号的特别用法:当输入目标路径时,如果在句尾敲一个星号再回车,则代表在输入的路径自动建立与源文件夹同名的一个子文件夹,复制到那里。可以与1结合使用,比如目标路径直接敲一个星号,则等于把源文件夹复制到桌面上的同名文件夹



3.要求重输:考虑到命令行窗口在删除一串长字符时非常不便,特此加入要求重输功能,当输入源路径或目标路径时,如果在句尾敲两个星号再回车,代表要求重新输入


4.添加演示模块:调用lib.bat时,如果没有什么特殊需求,可以直接调用“:demo”演示模块,将自动进行前序检查、拖拽路径与检查、计时、结果,不需要繁琐的再调用各个模块的写法,举例:
以下“碎文件拷贝.bat”
rem editable area
set "way_=:copy_normal"
::
rem ----------------------------------------
rem areas that do not normally need editing
cd /d "%~dp0"
set "lib_=lib.bat"
if not exist "%lib_%" exit
set "caller_=%~0"
::
call "%lib_%" :demo

5.添加计时的演示模块:我自己经常写一些自用的批处理,有时候想计一下运行时长,为了避免做重复工作,所以单拎了一个计时的演示模块,我自己经常在自用的其他批处理中调用。最长可以计60min,因为受dos命令的限制,如果计算超过60min算法会很复杂,还要考虑是否穿越12小时制的12点、是否穿越0点、是否穿越月底那天、是否闰年,等等等等变成了无底洞,已经违背了batch易取易用的初心,而且那么长时间的拷贝过于罕见,所以暂不考虑超过60min的统计。举例:
以下“随便其他什么.bat”
rem 先把lib.bat的位置定义了
set "lib_=哪\lib.bat"

@echo off
call "%lib_%" :demo_time

rem 这随便做些事情

call "%lib_%" :demo_time
pause

6.添加路径容错小模块:用于去掉输入路径中的双引号、问号、斜杠、大小于号、管道符号、星号,因为这些符号不允许出现在路径中,以此防止人为不小心输入。去掉路径前后多余的任意数量的空格和反斜杠


7.robocopy参数多加了一条移动

修复的问题:
1.修复路径中含有百分号时会造成问题的bug
2.修复当时间穿越整点时显示错误。

代码逻辑性更新:
1.整改了一些不规范的写法、变量命名方式,使代码逻辑更清晰,有利于维护
2.用一些for语句和小模块的形式,消除了一些赘述
3.在必要的部分使用setlocal和endlocal包装,使变量本地化,防止变量冲突和无关变量外溢
4.用一些skip等方法提高运行效率
5.取消定义命令的变量"%com_%",改用定义参数的变量"%parm_%",以防止延迟、不延迟造成的各种问题,举例:
以下“不拷贝小于1MB(1048576字节)的文件.bat”
rem editable area
set "parm_=/s /mt:128 /nodcopy /r:0 /min:1048576"
::
rem ----------------------------------------
rem areas that do not normally need editing
cd /d "%~dp0"
set "lib_=lib.bat"
if not exist "%lib_%" exit
set "caller_=%~0"
::
call "%lib_%" :demo

链接:https://pan.baidu.com/s/166Z6refd_4Xg_Xcncxedrg?pwd=b6i2

6Tom 发表于 2022-7-30 15:30

lizooo 发表于 2022-7-30 14:12
不会只有我一个人是奔着楼主的壁纸来的吧?好喜欢那只眼睛,所以,楼主,放全图!!!要原版,要高清!!! ...
啊哈哈,夹带了点私货被发现了,想要的话那就发出来吧,壁纸是王冰冰,但是现在她那边出了点状况,希望事情会好起来吧{:1_909:},要不以后这些壁纸贴图表情,都不是很有底气用了,满电脑贴的都是冰
https://attach.52pojie.cn//forum/202207/30/152151ho2ocgxxxqhfhrml.jpg?l

lizooo 发表于 2022-7-30 14:12

不会只有我一个人是奔着楼主的壁纸来的吧?好喜欢那只眼睛,所以,楼主,放全图!!!要原版,要高清!!!
{:301_995:}

tommyywq 发表于 2022-7-30 01:40

亲测好用,感谢大神分享!!~~

6Tom 发表于 2022-7-30 02:29

tommyywq 发表于 2022-7-30 01:40
亲测好用,感谢大神分享!!~~

过奖了,谢谢鼓励,更新9代了,可以重新下载一下

hfxiang 发表于 2022-7-30 12:20

这个要好好消化一下,楼主好像一下子发的见容太多。感谢分享

Shiliu2713 发表于 2022-7-30 13:23

咋没人评论,支持一下看看

nbwww 发表于 2022-7-30 17:14

下载下来研究研究谢谢

jstar 发表于 2022-9-17 16:14

神奇的操作
页: [1] 2
查看完整版本: 【Batch】给微软自家命令行程序robocopy做了个常用代码库