标题: [文件操作] 求助批处理批量解决移动指定类型文件到上一级目录中 [打印本页]
作者: mmm8422616 时间: 2024-4-10 10:58 标题: 求助批处理批量解决移动指定类型文件到上一级目录中
本帖最后由 mmm8422616 于 2024-4-10 11:00 编辑
多层级目录 最后一级目录下面有 .symlink .jgp .nfo 等等类型的文件 比如 D:\...\...\...\...\...\AAA\
求助批处理批量解决移动指定类型(比如.symlink)文件到上一级目录中,然后删除AAA下面的其他所有类型的其他文件(直接删除AAA文件夹及下面的文件)
哪位大大帮忙给实现一下啊 谢谢哦!
作者: 77七 时间: 2024-4-10 13:32
- @echo off
- cd /d "%~dp0"
- set str=symlink
- for /f "delims=" %%d in ('dir /b /s /ad ^|sort /r') do (
- move "%%d\*.%str%" "%%d\.."
- del /q "%%d\*"
- rd "%%d"
- pause
- exit
- )
复制代码
作者: mmm8422616 时间: 2024-4-10 15:35
回复 2# 77七
刚刚试了下 好像不对啊 把最后一级目录下的symlink移动了二级目录里面 然后其他类似的文件夹下的没有动
作者: ShowCode 时间: 2024-4-10 16:15
回复 3# mmm8422616
V1.bat- @echo off
- cd /d "%~dp0"
- set "ExtList=symlink jgp nfo"
- for %%i in (%ExtList%) do (
- call :ExecRelocate %%i
- )
- goto :eof
-
- :ExecRelocate
- for /f "delims=" %%d in ('dir /b /s /ad ^|sort /r') do (
- move "%%d\*.%1" "%%d\.."
- del /q "%%d\*"
- rd "%%d"
- pause
- exit /b
- )
复制代码
作者: ShowCode 时间: 2024-4-10 16:17
回复 3# mmm8422616
V2.bat- @echo off
- cd /d "%~dp0"
- set "ExtList=symlink jgp nfo"
- for /f "delims=" %%d in ('dir /b /s /ad ^|sort /r') do (
- for %%i in (%ExtList%) do (
- move "%%d\*.%%i" "%%d\.."
- )
- del /q "%%d\*"
- rd "%%d"
- pause
- exit /b
- )
复制代码
作者: 77七 时间: 2024-4-10 16:32
回复 3# mmm8422616
- @echo off
- cd /d "%~dp0"
- set str=symlink
- for /f "delims=" %%d in ('dir /b /s /ad') do (
- dir /b /ad "%%d" |findstr . 1>nul || set _"%%d"=1
- )
- for /f "delims=" %%d in ('dir /b /s /ad') do (
- if defined _"%%d" (
- if exist "%%d\*.%str%" (
- move "%%d\*.%str%" "%%d\.."
- del /q "%%d\*"
- rd "%%d"
- )
- )
- )
- pause
复制代码
作者: mmm8422616 时间: 2024-4-11 00:15 标题: 批处理批量解决移动指定类型文件到上一级目录中【已解决】
回复 6# 77七
感谢兄弟 已经测试完美解决我的问题 棒棒哒!
作者: qixiaobin0715 时间: 2024-4-11 08:45
本帖最后由 qixiaobin0715 于 2024-4-11 08:48 编辑
findstr有时是好用,但总觉得尽量少用为好,findstr属于外部命令,调用时其效率要差些,特别是在循环体内调用且又使用了管道命令效率更差些。- @echo off
- for /f "delims=" %%i in ('dir /s /b /ad') do (
- if exist "%%i\*.symlink" (
- set n=
- for /f "delims=" %%j in ('dir /b /ad "%%i"') do set n=true
- if not defined n (
- move "%%i\*.symlink" "%%i\.."
- rd /s /q "%%i"
- )
- )
- )
- pause
复制代码
以上代码未经测试,可能会有问题,但大体思路应当是这样。运行代码前请备份好源文件,以免出现意外。
作者: mmm8422616 时间: 2024-4-11 15:06
回复 8# qixiaobin0715
已经测试,没有问题,非常感谢大大们的帮助!!!
作者: aloha20200628 时间: 2024-4-11 15:21
回复 1# mmm8422616
一楼题意是指“最后一级目录(不再含子目录)”,如是则代码也许可进一步简化...
以下代码假设最后一级目录为 d:\a\aa\aaa- @echo off &set "_cd=d:\a\aa\aaa"
- cd /d "%_cd%" &(for %%F in ( "symlink" "jpg" "nfo" ) do move /y "*.%%~F" "..") 2>nul
- cd ".." & rd /s/q "%_cd%" &pause &exit/b
复制代码
作者: mmm8422616 时间: 2024-4-11 16:09
回复 mmm8422616
77七 发表于 2024-4-10 16:32
实际使用过程中发现了点小问题,麻烦大大给修改一下,比如目录下面有这几种类型的文件 .symlink .mp4 .mkv .nfo .jpg 等等的,我需要把.symlink .mp4 .mkv这几种类型的文件进行移动,然后再删除目录下的其他文件、再删除此文件夹。
现在的情况是,我移动了symlink类型的文件后,也删除了我想移动的mp4 mkv类型的文件了 当时没有考虑文件夹下有不同的影视文件类型。
麻烦大大给修改一下啊!
作者: mmm8422616 时间: 2024-4-11 16:13
回复 10# aloha20200628
这个好像不符合我的要求 你直接定义到了一个目录下了 实际上有很多大量这样类似的目录结构 需要批量进行
作者: 77七 时间: 2024-4-11 16:51
回复 11# mmm8422616
- @echo off
- cd /d "%~dp0"
- set str=symlink mp4 mkv
- for /f "delims=" %%d in ('dir /b /s /ad') do (
- dir /b /ad "%%d" |findstr . 1>nul || set _"%%d"=1
- )
- for /f "delims=" %%d in ('dir /b /s /ad') do (
- if defined _"%%d" (
- for %%x in (%str%) do (
- if exist "%%d\*.%%x" (
- move "%%d\*.%%x" "%%d\.."
- set _"%%d"=
- )
- )
- if not defined _"%%d" (
- del /q "%%d\*"
- rd "%%d"
- )
- )
- )
- pause
复制代码
作者: qixiaobin0715 时间: 2024-4-11 16:52
本帖最后由 qixiaobin0715 于 2024-4-11 16:58 编辑
- @echo off
- for /f "delims=" %%i in ('dir /s /b /ad') do (
- set n=
- for /f "delims=" %%j in ('dir /b /ad "%%i"') do set n=true
- if not defined n (
- set m=
- for /f "delims=" %%k in ('dir /b /a-d *.symlink *.mp4 *.mkv') do (
- set m=true
- move "%%i\%%k" "%%i\.."
- )
- if defined m rd /s /q "%%i"
- )
- )
- pause
复制代码
作者: aloha20200628 时间: 2024-4-11 18:35
本帖最后由 aloha20200628 于 2024-4-11 18:37 编辑
回复 12# mmm8422616
处理(删除)“多个” 末级目录和处理(删除)“每个” 末级目录可不是一回事
作者: 77七 时间: 2024-4-11 19:05
回复 8# qixiaobin0715
谢谢大佬指点!没考虑文件夹过多情况,如果文件夹数量成百上千、甚至过万,可能使用dir 都会影响效率;如果文件夹过多,目标文件且多存在于末级文件夹,使用dir /a-d 初步筛选可能符合条件的文件夹,可能更快点,具体看楼主实际情况吧:lol
作者: aloha20200628 时间: 2024-4-11 20:13
回复 16# 77七
单论判断一个指定目录(例如 C:\0\1\2\3)是否包含子目录,至少有三个方法如下
如果指定目录中确实没有子目录,各方法耗时差别几乎无感,但若真有好多多子目录,那差别就不一般了,方法三可是要用纯P去逐个遍历啊 所以中外老帖中还是用方法一或方法二为多。- ::方法一
- dir /b/ad "C:\0\1\2\3" | find /v "">nul || echo,是末级目录
- ::方法二
- dir /b/ad "C:\0\1\2\3" | findstr ".">nul || echo,是末级目录
- ::方法三
- set "n=" &for /f "delims=" %%j in ('dir /b/ad ""C:\0\1\2\3"') do set "n=1"
- if not defined n echo,是末级目录
复制代码
作者: 77七 时间: 2024-4-11 21:11
回复 17# aloha20200628
谢谢大佬指点!
如果某目录下子目录众多,使用call + for /d 及时退出遍历应该也行,不过没有dir 灵活,不能识别隐藏目录
- @echo off
- call :1 "C:\0\1\2\3"
- if %errorlevel% equ 2 (
- echo "C:\0\1\2\3" 是末级目录
- )
- pause & exit
-
- :1
- for /r "%~1" /d %%e in (*) do (
- exit /b 1
- )
- exit /b 2
复制代码
作者: aloha20200628 时间: 2024-4-11 21:57
本帖最后由 aloha20200628 于 2024-4-11 22:14 编辑
回复 12# mmm8422616
以下代码会处理当前目录下所有末级目录,但假设 “仅当末级目录中确实有文件被移动后” 才删除该末级目录...
代码中第3行可增减多个被空格分隔的用双引号包裹的文件扩展名字段- @echo off &setlocal
- (for /d /r %%d in (*) do dir /b/ad "%%~d\*" |find /v "">nul || (
- for %%F in ( "symlink" "jpg" "nfo" "mkv" "mp4") do if exist "%%d\*.%%F" (set/a "n=1"&move /y "%%d\*.%%F" "%%d\..")
- if defined n (set "n="&rd /s/q "%%d")
- )) 2>nul
- pause &exit/b
复制代码
作者: aloha20200628 时间: 2024-4-11 22:10
回复 18# 77七
老帖中对call的用法议论多多,其中还有慎用甚至不用的说法... 也难为了call先生,要对调用关键词在 "行首标签/内部命令/系统命令/外部(第三方)可执行脚本和程序文件/..." 中逐一匹配择取,也不知其匹配优先权如何,反正够累的
作者: qixiaobin0715 时间: 2024-4-12 08:24
回复 16# 77七
1.dir是内部命令,运行速度比我们想象的要快得多,定位到C:\,试了一下,25万多个文件夹也就是5分钟之内就解决问题。不知使用for /f+dir会如何;
2.文件夹超多的情况下,如果批处理使用6楼的思路,自定义变量设置数量可能会很多,甚至超限,效率可能更容易受到影响。记得win7系统变量设置最多可能8千多个,win10,win11不知是否放宽了限制。
作者: terse 时间: 2024-4-12 10:50
这里可以call一下 ,虽说不太喜欢用- @echo off
- set exr="symlink" "txt" "jpg" "doc"
- call :listdir "%cd%"
- pause
- exit
- :listdir
- for /d %%d in ("%~1\*") do set "d=%%d"&call :listdir "%%d"
- if defined d (
- set "n="
- for %%i in ( %exr%) do if exist "%d%\*.%%i" set n=1&move /y "%d%\*.%%i" "%d%\.." >nul 2>nul
- if defined n rd /s /q "%d%"
- )
- set "d="
复制代码
作者: 77七 时间: 2024-4-12 11:16
我测试了一下,只判断、打印、计数末级目录,以我的c盘实际情况为例,共有末级目录2200个- 17楼方法一
- 11:05:21.13
- 11:05:54.99
- 耗时 33s
-
- 17楼方法二
- 10:56:52.10
- 10:57:31.35
- 耗时 39s
-
- 17楼方法三
- 10:55:53.43
- 10:56:24.59
- 耗时 31s
-
- 18楼方法
- 10:59:32.28
- 10:59:45.13
- 耗时 13s
复制代码
作者: 77七 时间: 2024-4-12 15:39
回复 22# terse
大佬,我在测试效率的时候发现,遇到文件夹名称,如 新建文件夹 (2),则会出错- @echo off
- call :listdir "%cd%"
- pause
- exit
- :listdir
- for /d %%d in ("%~1\*") do set "d=%%d"&call :listdir "%%d"
- if defined d (
- echo %d%
- )
- set "d="
复制代码
echo %d%结果,右括号消失了
复制代码
作者: 77七 时间: 2024-4-12 15:53
本帖最后由 77七 于 2024-4-12 16:20 编辑
回复 24# 77七
我找到原因了,去掉()则正确复制代码
这个if 和 () 真是纠缠不清啊,这都能出错...这个直接颠覆了我的认知,以前一直以为明着写才会出错
再次验证
- @echo off
- set str=D:\\1\新建文件夹 (2)
- if defined str (
- echo %str%
- )
- pause
复制代码
结果还是错的
幸好以前跟aloha20200628大佬学了一招,使用set /p=""<nul,保留特殊字符,双引号直接把()打出原形
- @echo off
- set str=D:\\1\新建文件夹 (2)
- if defined str (
- set /p="%str%"<nul
- echo=
- )
- pause
复制代码
结果正确
想到aloha20200628大佬提到 set /p=""<nul无法保留前置空格,看来也不是个完美办法
- @echo off
- set str= D:\\1\新建文件夹 (2)
- if defined str (
- for /f "delims=" %%x in ("%str%") do (
- echo %%x
- )
- )
- pause
复制代码
难道只能这样了?...
作者: terse 时间: 2024-4-12 16:22
本帖最后由 terse 于 2024-4-12 16:34 编辑
回复 25# 77七
加引号
其实这里是开了延迟的,可以!str!这样!str!
作者: 77七 时间: 2024-4-12 16:32
回复 26# terse
谢谢大佬指点!引号确实能解决此例中的显示问题,但我想到遇到其它问题的时候可能不需要引号
我又试着开启变量延迟扩展,结果正确
- @echo off
- set str=D:\\1\新建文件夹 (2)
- if defined str (
- setlocal enabledelayedexpansion
- echo !str!
- endlocal
- )
- pause
复制代码
作者: terse 时间: 2024-4-12 16:36
回复 27# 77七
是的,这里是开了延迟的,可以!str!
作者: 77七 时间: 2024-4-12 16:48
本帖最后由 77七 于 2024-4-12 16:52 编辑
回复 28# terse
谢谢大佬指点!特殊字符与变量延迟扩展的关系真是奇妙!开启变量延迟扩展不当则会漏掉特殊字符“!”本身,但更多情况是成就了更多特殊字符。
作者: aloha20200628 时间: 2024-4-13 18:11
本帖最后由 aloha20200628 于 2024-4-13 18:28 编辑
用本帖敲打一下 call 的功过,还是要看比较实际的测试结果,先看用于测试比对的四种方法,其各自代码如下
::方法1- set "n="&for /d /r %%d in (*) do (
- dir /b/ad "%%~d\*" 2>nul | find /v "">nul || set/a "n+=1"
- )
- echo,发现 %n% 个末级目录&exit/b
复制代码
::方法2- set "n="&for /d /r %%d in (*) do (
- dir /b/ad "%%~d\*" 2>nul | findstr ".">nul || set/a "n+=1"
- )
- echo,发现 %n% 个末级目录&exit/b
复制代码
::方法3- set "n="&for /d /r %%d in (*) do (
- set "k=" &for /f "delims=" %%a in ('dir /b/ad "%%~d\*" 2^>nul') do set "k=1"
- if not defined k set/a "n+=1"
- )
- echo,发现 %n% 个末级目录&exit/b
复制代码
::方法4- set "n="&for /d /r %%d in (*) do (
- set "k=" &(call :xxx "%%~d" k)
- if not defined k set/a "n+=1"
- )
- echo,发现 %n% 个末级目录&exit/b
- :xxx
- for /r "%~1" /d %%e in (*) do (set "%~2=1"&exit/b)
- exit/b
复制代码
看如下测试结果
测试主目录及其文件和子目录体量 方法1耗时/方法2耗时/方法3耗时/方法4耗时(耗时单位=秒)
c:\a1 148,814 个文件,32,751 个文件夹 255/275/783/1149
c:\a2 30,262 个文件, 3,281 个文件夹 21/24/26/14
c:\a3 28,159 个文件, 2,114 个文件夹 13/15/15/6
c:\a4 16,916 个文件, 1,213 个文件夹 8/9/8/2
c:\a5 1,248 个文件, 717 个文件夹 4/5/4/1
从以上结果数据可见,小体量样本测试证明方法4有明显优势,但大样本比对就出现了逆转。
至少两点结论》
其一是用方法3即纯P跟读目录列表的效率会随样本体量增大锐减(此点其实是老生常谈不足为奇);
其二是“call乃效率大杀器”的杀气要在足够大体量的数据样本中才会凸显,在a1和a2体量之间存在一个令call从“英雄变狗熊”的拐点...
作者: 77七 时间: 2024-4-13 18:36
本帖最后由 77七 于 2024-4-13 18:55 编辑
回复 30# aloha20200628
大佬,for /r "%~1"是我随手写的,如果目录非常庞大,dir /b /ad 没有/s ,没有遍历子目录,显然有失公平,使用/r应该徒增了很多无用功吧,以前我做过测试,for /r 的遍历机制,当处理到第一个(还是第二个,记不清了)目录的时候,大概已经完成了700多个目录的列举,(数据记得不是很清楚了)不知道这是否拉低了效率
麻烦大佬您再测试一下,for /d
- set "n="&for /d /r %%d in (*) do (
- set "k=" &(call :xxx "%%~d" k)
- if not defined k set/a "n+=1"
- )
- echo,发现 %n% 个末级目录&exit/b
- :xxx
- for /d %%e in ("%~1\*") do (set "%~2=1"&exit/b)
- exit/b
复制代码
作者: 77七 时间: 2024-4-13 19:29
本帖最后由 77七 于 2024-4-13 19:31 编辑
回复 30# aloha20200628
大佬,我把c盘复制了5次66000文件,16000目录,末级目录10000个
测试了方法1、4,结论和大佬的一致
call +for /d 7分钟,dir 4分钟。call cpu占用比dir低。感觉dir在全力跑,call+for /d有点散步的意思
作者: aloha20200628 时间: 2024-4-13 20:35
本帖最后由 aloha20200628 于 2024-4-13 22:11 编辑
回复 31# 77七
只要用call方法
for /d %%d in ("%~1\*") do (set "%~2=1"&exit/b)
与
for /d /r "%~1" %%d in (*) do (set "%~2=1"&exit/b)
两个句式的耗时差约在 0.07~0.05%,二者基本相当吧。
结论还是》一方面,得益于用call方法能使纯P代码从for循环中及时退出;另一方面,跑大体量数据到其“拐点”后就“怂”了...
作者: terse 时间: 2024-4-13 23:11
这样CALL,系统开销是否会有所不同呢- @echo off
- call :listdir "%cd%"
- echo %n%
- pause
- exit
- :listdir
- for /d %%d in ("%~1\*") do (
- set "d=%%d"
- call :listdir "%%d"
- )
- if defined d (
- set /an+=1
- )
- set "d="
复制代码
作者: 77七 时间: 2024-4-14 00:55
本帖最后由 77七 于 2024-4-14 01:00 编辑
回复 34# terse
之前测试效率,被if () 搞中断了...
现在我测试了一下,大佬您的代码效率很高,测试样本仍然是32楼的那些,耗时20秒左右
样本说明:复制了整个c盘小于10kb的文件及目录,并手动复制了几次
可能是因为样本比较特殊,使用中出现了一些小插曲,大佬的代码遗漏了 ”用户\admin\appdata“ 目录下的末级目录
,到- "f:\测试\Users - 副本 - 副本 (2)\admin\3D Objects"
复制代码
就结束了。
而dir 和call +for /d 均列出了
- "F:\测试\Users - 副本 - 副本\admin\AppData\Local\Microsoft\PowerShell\7.2.7"
复制代码
作者: 77七 时间: 2024-4-14 01:28
本帖最后由 77七 于 2024-4-14 01:57 编辑
我找出原因了
用户\admin\appdata
这个目录使用for /r /d 能够遍历出来,使用for /d 则不能
惭愧惭愧,学习不思考......appdata下的子目录不具有隐藏属性,所以for /r /d遍历了appdata下的子目录,仍没有遍历出appdata
作者: aloha20200628 时间: 2024-4-14 18:54
本帖最后由 aloha20200628 于 2024-4-14 19:04 编辑
用本帖到目前为止的3种典型的call方法(代码如下)再跑了几遍测试样本,与30楼所列方法1-3测试数据合并,能有一个更全面的结论...致谢30楼和36楼- ::方法4
- set "n="&for /d /r %%d in (*) do (
- set "k=" &(call :x4 "%%~d" k)
- if not defined k set/a "n+=1"
- )
- echo,发现 %n% 个末级目录&pause&exit/b
- :x4
- for /d /r "%~1" %%d in (*) do (set "%~2=1"&exit/b)
- exit/b
复制代码
- ::方法5
- set "n="&for /d /r %%d in (*) do (
- set "k=" &(call :x5 "%%~d" k)
- if not defined k set/a "n+=1"
- )
- echo,发现 %n% 个末级目录&pause&exit/b
- :x5
- for /d %%d in ("%~1\*") do (set "%~2=1"&exit/b)
- exit/b
复制代码
- ::方法6
- set "n="&(call :x6 "%cd%")
- echo,发现 %n% 个末级目录&pause&exit/b
- :x6
- for /d %%d in ("%~1\*") do (set "d=%%d"&call :x6 "%%d")
- if defined d (set /a n+=1)
- set "d="&exit/b
复制代码
方法1-6测试数据合并如下
测试主目录及其文件和子目录体量 方法1耗时/方法2耗时/方法3耗时/方法4耗时/方法5耗时/方法6耗时(耗时单位=秒)
c:\a1 148,814 个文件,32,751 个文件夹 255/275/783/1239/1149/741
c:\a2 30,262 个文件, 3,281 个文件夹 21/24/26/14/14/4
c:\a3 28,159 个文件, 2,114 个文件夹 13/15/15/6/6/2
c:\a4 16,916 个文件, 1,213 个文件夹 8/9/8/2/2/1
可见方法6采用了递归调用,没有方法4-5代码中宿主循环开销,效率倍增,但依然会在a1-a2样本之间遭遇 ‘拐点爆胎’,被方法1-2赶超...
另一点是如36楼所说会遗漏那些受保护(如隐藏)目录...
》以上仅供参考,有兴趣者还可参考以下老帖,融合体会一下call先生的功与过...
一。批处理之家 » BAT教程&资料 » 讨论:批处理运行效率浅谈
二。http://bbs.bathome.net/thread-4482-1-1.html
作者: terse 时间: 2024-4-14 19:51
回复 37# aloha20200628
差距这么大,也许环境和目录结构的不同,测试结构也会不同,下面代码创建的空目录试了一下,相比较还好CALL并不是太耗资源,- for /l %%a in (1,1,10) do for /l %%b in (1,1,10) do for /l %%c in (1,1,10) do for /l %%d in (1,1,10) do for /l %%e in (1,1,10) do md test\%%a\%%b\%%c\%%d\%%e\ 2>nul
复制代码
欢迎光临 批处理之家 (http://www.bathome.net/) |
Powered by Discuz! 7.2 |