Board logo

标题: [未彻底解决]多个文件拖到cmd窗口后,如何获取每一文件的完整路径 [打印本页]

作者: namejm    时间: 2010-4-5 23:44     标题: [未彻底解决]多个文件拖到cmd窗口后,如何获取每一文件的完整路径

  cmd窗口有一个特点:支持把文件拖曳到cmd窗口中来处理。

  编个十分简单的脚本来演示一下:
  1. @echo off
  2. echo %1
  3. pause
复制代码
  一般的情形是这样的:

  1、当文件的完整路径中带有空格的时候,cmd窗口将自动为文件的完整路径首尾添加一对引号。例如,把 d:\abc xyz 文件夹下的 test.txt 文件拖曳到以上代码所在的脚本文件上之后,将在屏幕上显示: "d:\abc xyz\test.txt"
  2、当文件的完整路径中没有空格的时候,cmd窗口将不会为文件的完整路径添加引号对。例如:把 d:\abcxyz 文件夹下的 test.txt 文件拖曳到以上代码所在的脚本文件上之后,将在屏幕上显示: d:\abcxyz\test.txt。请注意,这个时候,文件路径里没有引号对。

  当然,这只是一般情形。还有一种特殊情形:当文件的完整路径中有&但是不带空格的时候,以上代码将捕获不到正确的路径。比如:把 d:\abcxyz 文件夹下的 test1&test2.txt 文件拖曳到以上代码所在的脚本文件上之后,屏幕上显示的不是 d:\abcxyz\test1&test2.txt,而是显示: d:\abcxyz\test1,也就是说,&后的字符串直接被丢弃掉了。这可能是cmd窗口在捕获or传递参数时的一个bug。

  假设 d:\test 目录下有如下文件,现在把它们同时拖曳到脚本文件上来执行,需要获得每一个文件的完整路径,请问如何编写代码?
  1. abc.txt
  2. abc xyz.txt
  3. abc^xyz.txt
  4. abc&xyz.txt
  5. abc & xyz.txt
  6. (abc)(xyz).txt
  7. (abc) (xyz).txt
  8. (abc)&(xyz).txt
  9. (abc) & (xyz).txt
  10. abc;xyz.txt
  11. abc,xyz.txt
  12. abc=xyz.txt
复制代码
  想到这个问题,是源于这个帖子的讨论: 如何用批处理批量移动文件或文件夹?(http://bbs.bathome.net/thread-7504-1-1.html

  提示:一般情况下,可以用如下代码显示多个文件的完整路径:
  1. @echo off
  2. echo %*
  3. pause
复制代码
=====================================================
qzwqzw 在7楼给出了解释:
用批处理的办法基本无解

这个问题源于Explorer的拖放扩展程序shell32.dll
注册表路径位于HKEY_CLASSES_ROOT\CLSID\{86C86720-42A0-1069-A2E8-08002B30309D}
因为它没有将“&"视为转义符号
所以不会用引号对将其扩起取消转义

所以对于这个问题无非以下几个方案:
1、修改拖放扩展程序shell32.dll,让它可以处理"&"的情况;
2、新建拖放扩展程序,专门处理cmd之类程序的路径过滤;
3、在处理前告知用户使用"&"的情况可能出错,交由用户处理发生的异常;

  既然这样,那么,用批处理来做的话,只好不去兼容带&但不同时带空格的完整路径这一情况了(路径中带有^符号也不行,头疼ing)。演示代码如下:
  1. @echo off
  2. set str=%*
  3. set "str=%str:"=%"
  4. set "str= %str%"
  5. call set "str=%%str: %~d1=" "%~d1%%"
  6. for %%i in ("%str%") do echo %%i
  7. pause
复制代码

作者: solitude    时间: 2010-4-5 23:52

是%1么?
我记得好像是%0
作者: lxzzr    时间: 2010-4-6 00:47

能提取这样的:abc & xyz.txt,却不能提取abc&xyz.txt,郁闷....

题是好题....
作者: mm546863903    时间: 2010-4-6 08:14

额,有点难度啊,思考中。。。
作者: GNU    时间: 2010-4-6 09:29     标题: 回复 2楼 的帖子

相当明显,你记错了。
作者: caruko    时间: 2010-4-6 09:50

既然是拖到窗口...那么就是系统自动完成的...
要在这个阶段用BAT脚本插手是没办法的..
只能在接受到1%以后,再做路径判断..有问题自动补偿完整
作者: qzwqzw    时间: 2010-4-6 11:41

用批处理的办法基本无解

这个问题源于Explorer的拖放扩展程序shell32.dll
注册表路径位于HKEY_CLASSES_ROOT\CLSID\{86C86720-42A0-1069-A2E8-08002B30309D}
因为它没有将“&"视为转义符号
所以不会用引号对将其扩起取消转义

所以对于这个问题无非以下几个方案:
1、修改拖放扩展程序shell32.dll,让它可以处理"&"的情况;
2、新建拖放扩展程序,专门处理cmd之类程序的路径过滤;
3、在处理前告知用户使用"&"的情况可能出错,交由用户处理发生的异常;
作者: netbenton    时间: 2010-4-6 18:56     标题: ¥¥¥重大发现¥¥¥!!!!!!

不带空格的目录中含有&号时,&会把后面字符串的作为一个新的项目去执行!
前面没了%0,只是字符串单独作为一个新命令行的在裸奔!!!

如下面的目录:

“新建文件夹&notepad&notepad&notepad”

当拖曳到一楼的示范批处理,在按了任意键之后,会连续打开3次记事本!!!!

神吧!!!目录也可以作为命令去运行程序了
作者: yangfengoo    时间: 2010-4-6 21:59     标题: 回复 8楼 的帖子

作用实在有限,恶作剧或者掩饰命令还可以
作者: zqz0012005    时间: 2010-4-9 21:09

应该算是大家都知道的老经验了吧,对于%x参数引用较保险的做法是"%~x"
怎么又重新讨论了呢?
作者: netbenton    时间: 2010-4-9 22:02

不是那个意思了,其实这个问题是不可能解决的,因为有&在路径中后,系统把成多个任务去看了,
像这个:
“...\新建文件夹&notepad&notepad&notepad”
把它拖到:abc.bat后
其实在cmd窗口执行了四次任务了:
1,   abc.bat ...\新建文件夹
2,  notepad
3,  notepad
4,  notepad

如此一来,又怎么可能让abc.bat得到完整的路径参数呢?系统根本就没有打算给它。
作者: 523066680    时间: 2010-4-9 22:26

  1. @echo off
  2. :a
  3. if not "%~1"=="" (echo "%~1" &shift /1 &goto :a)
  4. pause
复制代码
没看出来……


啊我现在才知道可以多个文件同时拖放并获取各个文件名,OUT了……
  !!!!
-O_o - 楼上本顿的技术飚到30了呵
_[  ]__

[ 本帖最后由 523066680 于 2010-4-9 22:38 编辑 ]
作者: Spring    时间: 2010-4-9 23:46

原帖由 zqz0012005 于 2010-4-9 21:09 发表
应该算是大家都知道的老经验了吧,对于%x参数引用较保险的做法是"%~x"
怎么又重新讨论了呢?

在 D 盘根目录新建一个文本文档,命名为 "R&B.txt" ,也就是创建一个文本文档 “D:\R&B.txt” ,
现在讨论的问题是把它拖到一个内容为

@echo off
echo %*
pause


的批处理文件上,怎样能使批处理能显示 D:\R&B.txt
作者: todayambition    时间: 2010-4-10 10:33

@echo off
echo %*
pause
中用%*接收全部参数都没有得到abc&xyz.txt,应该是不行的
不过,把要此批处理显示路径的所有文件放到一个文件夹(如:d:\temp\)下,再dir d:\temp\*.txt /b
就得到了所要的路径
作者: x9tiancmd    时间: 2010-4-10 23:10

发觉大家,都有点太为难 set/p 命令了
  

           哈哈, 不过 也只有这样才能长知识长见识○( ̄﹏ ̄)○
作者: namejm    时间: 2010-4-10 23:27

原帖由 todayambition 于 2010-4-10 10:33 发表
@echo off
echo %*
pause
中用%*接收全部参数都没有得到abc&xyz.txt,应该是不行的
不过,把要此批处理显示路径的所有文件放到一个文件夹(如:d:\temp\)下,再dir d:\temp\*.txt /b
就得到了所要的路径

  把多个文件拖曳到cmd窗口来处理,其实是添加了人机互动的环节,能带来更大的灵活性,这是用 dir 命令所无法企及的。
作者: todayambition    时间: 2010-4-11 11:59     标题: 回复 16楼 的帖子

呵呵 namejm说的没错,而且我发现c语言也有这功能,有意思。
我的原意是将多个文件放入一个文件夹,然后将文件夹拖到cmd窗口,然后用dir %1得到所要的文件路径,避免了显示如abc&xyz.txt这样名字的文件的路径时出错
作者: GNU    时间: 2010-4-11 12:18

我始终认为,批处理就应该是一次双击全部搞定,拖放=蛋疼。
作者: cjiabing    时间: 2010-8-20 22:05

经过一个下午的努力,终于有了结果,效率不是很高,但还是基本能够实现目标。第一次来挑战区,请多多指教。
  1. echo.>abc.txt
  2. echo.>"abc xyz.txt"
  3. echo.>"abc^xyz.txt"
  4. echo.>"abc&xyz.txt"
  5. echo.>"abc & xyz.txt"
  6. echo.>"(abc)(xyz).txt"
  7. echo.>"(abc) (xyz).txt"
  8. echo.>"(abc)&(xyz).txt"
  9. echo.>"(abc) & (xyz).txt"
  10. echo.>"abc;xyz.txt"
  11. echo.>"abc,xyz.txt"
  12. echo.>"abc=xyz.txt"
复制代码
可以同时拖拉多个文件到批处理中,最后获取这几个文件的完全合格文件名。
文件名含有空格的也可以实现。
但文件名中含有(.)与(&)的,后缀名非三位(如.db、html)的无法读取——需要重新设置。
不处理文件夹。

已经支持的后缀名有:txt;doc;exe;rar;mp3;jpg;wma;bat,其它格式需补充。
可能与 “echo %*”有关,重定向时自动放到“%USERPROFILE% ”中去了,只好用 “>>%~dp0”。

由于用了两对重复的FOR,特别是设置“hzm”的FOR,导致速度下降。
应该也支持从单个文本内容中提取路径名。方法是将“~dp0_2tyxs.txt”改为该文本名称。
其它方面有待完善。

  1. @echo off&setlocal enabledelayedexpansion
  2. echo %*>>"%~dp0_test.txt"
  3. echo.>"%~dp0_test2.txt"
  4. echo.>"%~dp0_results.txt"
  5. for /f "usebackq tokens=*" %%i in ("%~dp0_test.txt") do (
  6. set var=%%i
  7. for /l %%a in (0,1,1000) do (
  8. set /a ddl=%%a-1
  9. set str2=!var:~%%a,2!
  10. if "!str2!"==":\" call echo %%var:~!ddl!,1%%!var:~%%a,1000!>>"%~dp0_test1.txt"
  11. )
  12. )
  13. for %%l in (txt;doc;exe;rar;mp3;jpg;wma;bat;htm;pdf;ppt) do (
  14. set hzm=%%l
  15. for /f "usebackq tokens=*"  %%i in ("%~dp0_test1.txt") do (
  16. set var=%%i
  17. for /l %%a in (0,1,1000) do (
  18. set /a ddf=%%a+4
  19. set str=!var:~%%a,4!
  20. if "!str!"==".!hzm!" echo !var:~0,%%a!.!hzm!>>"%~dp0_test2.txt"
  21. )
  22. )
  23. for /f "usebackq tokens=1,2,3,4 delims=." %%a in ("%~dp0_test2.txt") do (
  24. set vara=%%a
  25. set varb=%%b
  26. if /i "!varb:~0,3!"=="!hzm!" echo !vara!.!varb:~0,3!>>"%~dp0_test3.txt"
  27. )
  28. )
  29. for /f "usebackq delims=" %%a in (`sort "%~dp0_test3.txt"`) do (
  30. if not %%a equ !b! echo %%a>>"%~dp0_results.txt"
  31. set b=%%a)
  32. del "%~dp0_test.txt"
  33. del "%~dp0_test1.txt"
  34. del "%~dp0_test3.txt"
  35. del "%~dp0_test2.txt"
  36. start "" "%~dp0_results.txt"
  37. EXIT
复制代码

[ 本帖最后由 cjiabing 于 2010-8-21 10:42 编辑 ]
作者: cjiabing    时间: 2010-10-9 21:04

晕掉,以前长篇大论都没达到效果,以下几个字就解决了!~
因为用的是FOR,不支持那些变态文件,但基本上能满足一般使用了,比如拖入MP3播放。
  1. @echo off
  2. for /f "tokens=*" %%a in ("%*") do (
  3. for %%i in (%%a) do if exist %%i echo %%i
  4. )
  5. pause
复制代码

作者: CrLf    时间: 2011-2-6 23:56

楼上这么麻烦干嘛?一句话的事:
for %%a in (%*) do echo %%~fa

这样说来,难道以后拖放只能用set /p了吗?
set /p n=&&for /f %%a in ("%n:"=%") do echo %%~fa
作者: yhcbird    时间: 2011-2-10 17:28

原帖由 GNU 于 2010-4-11 12:18 发表
我始终认为,批处理就应该是一次双击全部搞定,拖放=蛋疼。


是啊,太蛋疼了,批处理如果要进行要拖放失去了批处理的快捷高效。。。。一点意思都没有。。。
作者: slore    时间: 2011-3-25 17:32

用批处理的办法基本无解

这个问题源于Explorer的拖放扩展程序shell32.dll
注册表路径位于HKEY_CLASSES_ROOT\CLSID\{86C86720-42A0-1069-A2E8-08002B30309D}
因为它没有将“&"视为转义符号
所以不会用引号对将其 ...
qzwqzw 发表于 2010-4-6 11:41



说下,和shell32.dll没关系,明显是cmd的事情。。。
作者: qzwqzw    时间: 2011-4-29 16:26

本帖最后由 qzwqzw 于 2011-4-29 16:30 编辑
说下,和shell32.dll没关系,明显是cmd的事情。。。
slore 发表于 2011-3-25 17:32

呵呵
这可真是公说公有理婆说婆有理了
cmd怪shell32不把&用引号包裹住再交过来
shell32怪cmd自己不把赤裸的&管教好
一嘴烂账没法说清

不过又回头多想了一下
这个问题仍有探索的余地
余地就在%cmdcmdline%
那里包裹了更多的信息
想办法处理了一下
因为以前很少关注转义字符的处理
费了许多功夫才大致完成
最终的结果是给定的12个特殊文件处理了11个
唯一剩下的一个(abc)&(xyz).cmd
没什么好办法
因为它直接导致批处理无法启动
  1. :: 测试多个怪僻文件名直接拖拽到批处理文件上,批处理是否正确处理这些文件名
  2. @echo off
  3. rem pause
  4. if not [%1]==[] goto :start
  5. echo 正在生成测试文件
  6. echo.>abc.cmd
  7. echo.>"abc xyz.cmd"
  8. echo.>"abc^xyz.cmd"
  9. echo.>"abc&xyz.cmd"
  10. echo.>"abc & xyz.cmd"
  11. echo.>"(abc)(xyz).cmd"
  12. echo.>"(abc) (xyz).cmd"
  13. rem echo.>"(abc)&(xyz).cmd"
  14. echo.>"(abc) & (xyz).cmd"
  15. echo.>"abc;xyz.cmd"
  16. echo.>"abc,xyz.cmd"
  17. echo.>"abc=xyz.cmd"
  18. echo @echo 溢出漏洞存在!^&pause>xyz.cmd
  19. echo 测试文件生成.
  20. goto :eof
  21. :start
  22. echo starting...
  23. set _line="%cmdcmdline:"="%"
  24. set _line=%_line:^=^%
  25. set _line=%_line:&=&%
  26. set _line=%_line:(=(%
  27. set _line=%_line:)=)%
  28. set _line=%_line:,=,%
  29. set _line=%_line:;=;%
  30. set _line=%_line: =□%
  31. rem set _line
  32. rem pause
  33. setlocal EnableDelayedExpansion
  34. for %%f in (%_line:~1,-1%) do set _line2=!_line2!=%%f
  35. endlocal & set _line=%_line2:□= %
  36. set _line=%_line:"="%
  37. set _line=%_line:~9,-1%
  38. rem set _line
  39. rem pause
  40. setlocal EnableDelayedExpansion
  41. for %%f in (%_line%) do (
  42. set _file="%%~f"
  43. set _file=!_file:^=^^!
  44. set _file=!_file:&=^&!
  45. set _file=!_file:(=^(!
  46. set _file=!_file:)=^)!
  47. set _file=!_file:,=,!
  48. set _file=!_file:;=;!
  49. set _file=!_file:===!
  50. if exist !_file! (echo ---!_file!---) else (echo ___"%%~f"____)
  51. )
  52. endlocal
  53. echo 结束
  54. pause
复制代码
另外
转义字符的处理过程中
又遇到了等号的替换问题
顺便提供了一种新思路
  1. @echo off & setlocal EnableDelayedExpansion
  2. set _exp=var=value
  3. set _exp
  4. set _exp=%_exp:===%
  5. set _exp
  6. echo 全角等号可以直接替换
  7. pause
  8. set _exp=var=value
  9. set _exp
  10. for %%f in ("=") do (
  11. call set _exp=!_exp:%%~f=-!
  12. )
  13. set _exp
  14. echo 半角等号不能直接替换
  15. pause
  16. set _exp=var=value
  17. set _exp
  18. set _exp=%_exp: =□%
  19. set _exp=%_exp:;=;%
  20. for %%e in (%_exp%) do set _exp2=!_exp2!=%%e
  21. set _exp=%_exp2:□= %
  22. set _exp=%_exp:;=;%
  23. set _exp=%_exp:~1%
  24. set _exp
  25. echo 转折的办法:把非等号的for分隔符替换掉,再用for遍历被等号分隔的所有串,并且用等号的替换字符连接这些串,最后将其它分隔符恢复,不支持处理连续的等号
  26. pause
复制代码

作者: zqz0012005    时间: 2011-4-29 19:39

qzw居然把我那个例子拿出来了,这应该是cmd.exe自身的BUG,解析命令行有缺陷。应该无解,除非让微软打补丁。

替换等号那个,无法兼容特殊字符。对特殊字符的处理存在不足是cmd.exe的根本缺陷。只有研究它的源码才知道问题在何处。

PS:最近工作中经常涉及对字符串的解析与处理,发现不少问题,比如内存泄露、踩内存、逻辑问题之类的,这玩意的确很让人头疼。
作者: qzwqzw    时间: 2011-4-30 19:51

qzw居然把我那个例子拿出来了
zqz0012005 发表于 2011-4-29 19:39

恕我无知
竟然不知道那段文字引用了你哪个例子

关于等号替换
我认为稳妥的办法还是逐个字符检测
特殊字符可以另行处理
  1. @echo off
  2. set _exp=if "%%a%%"=="%%b%%" echo 2^^3=8^>out.txt
  3. set _exp
  4. set "_exp=%_exp:"="%"
  5. :loop
  6. if "%_exp:~0,1%"=="=" (
  7. set "_exp2=%_exp2%="
  8. ) else (
  9. set "_exp2=%_exp2%%_exp:~0,1%"
  10. )
  11. set "_exp=%_exp:~1%"
  12. if not "%_exp%"=="" goto :loop
  13. set "_exp=%_exp2:"="%"
  14. set _exp2=
  15. set _exp
  16. echo 老实的办法:逐个字符判断是否等号,是则替换,不是则直接连接
  17. pause
复制代码

作者: zqz0012005    时间: 2011-5-5 23:40

原来兄不在论坛管理群,倒忘了以前在群里是哪几个人讨论过此问题。
当时我用的例子和你这个类似(另外还用了VBS监控进程创建来观察%cmdcmdline%),真是英雄所见略同。
作者: techon    时间: 2011-5-9 22:40

本帖最后由 techon 于 2011-5-10 01:45 编辑

原来是这样的。。。只要路径或文件名中有空格 就会加上引号从而处理任何文件名,
路径和文件名都没有空格,但是有特殊符号时 如 & ^ % 等 则会出现错误。。。

怪不得我在桌面上找了一堆文件都没问题呢
作者: powerbat    时间: 2011-5-9 23:32

为什么不试试这个:
(abc)&(xyz).txt
作者: techon    时间: 2011-5-10 00:17

本帖最后由 techon 于 2011-5-10 01:51 编辑

这个cmd  shell 不给力 改天试试 PowerShell
作者: techon    时间: 2011-5-10 00:53

本帖最后由 techon 于 2011-5-10 02:16 编辑

没什么好办法了?。。。
作者: cjiabing    时间: 2011-6-3 13:24

本帖最后由 cjiabing 于 2011-6-3 13:27 编辑

21# zm900612
忘记怎么说了,从实践总结出来的!~
或许,外面的FOR真的多余了,有空研究清楚了再说说道理!~
作者: cjiabing    时间: 2012-2-23 00:32

本帖最后由 cjiabing 于 2012-2-23 00:35 编辑

我弱弱地问,以下代码满足了楼主的要求没有?
首先,用dir来获得文件的路径(我经常用if 、dir、%fa来干这种事),有点罗嗦,对文件夹无效,可惜无法用if exist。
第二、既然%*能保存所有参数,那么把这些含有特殊符号的参数全部输入到dir,而dir是不会对文件进行操作的,所以它也可以保存内容。
第三、for虽然也是逐行逐个处理,遇到特殊符号会发生错误。但对于集合内的命令执行结果它们却视而不见,你可以先ipconfig再for,而不必考虑它输出来的是什么。
  1. @echo off
  2. ::显示所有拖入文件的路径,完美支持楼主提供的含有特殊符号的文件名。唯一不足的是不支持文件夹,文件夹需要另外想办法。
  3. echo     以下是文件:
  4. dir /b %*
  5. pause
  6. ::计算拖入文件的个数,用来验证。
  7. dir /b %*|findstr /n /i .*
  8. pause
  9. @echo off&setlocal enabledelayedexpansion
  10. ::决定还是用FOR来验证,避免无法继续下一步操作。
  11. set num=0
  12. for /f "tokens=*" %%a in ('dir /b %*') do set /a num+=1&echo;!num!:%%a
  13. pause
复制代码

作者: 幼稚园    时间: 2012-8-10 08:36

本帖最后由 幼稚园 于 2012-8-10 09:34 编辑

果然好难啊!
29楼的(abc)&(xyz).txt.txt拖到批处理上,在传递参数时就发生错误了!
潜水学习......
作者: qwe274208829    时间: 2012-8-15 21:56

顶下
作者: flaven    时间: 2013-7-4 13:00

回复 33# cjiabing

建了个文件,名为 4~@%#$!^&=().jpg,拖进去不行。。。
作者: cjiabing    时间: 2013-7-5 21:55

回复 36# flaven


    奇怪,我放在桌面上可以执行,放到驱动盘下就出错了!~
    可能路径名含有空格的时候它自动添加双引号,没有空格的时候它没有添加,所以出错了!~

原理是这样的:
1.对于“%*”直接使用    "%*"    (即echo "%*")即可解决问题,但问题是这个结果中的路径都挤到了一行里面,不方面引用。
因此需要考虑第二个问题:拆分路径并去掉双引号。
2.一般情况下,利用dir命令的特点来自动拆分路径和双引号,所以使用  dir /b %*  即可。但现在看来也是存在一定限制。
3.实在不行的情况下,又得回头使用SET替换的方式,或者使用FOR替换的方式,那样会比较麻烦点。
因为文件名含有特殊符号,所以使用双引号,但为了提前单个独立的路径又需要去掉双引号,这是一个矛盾,解决了这个问题就可以解决了这个难题。
作者: flaven    时间: 2013-7-7 15:03

回复 37# cjiabing

上周我遇到类似问题,搞了一星期试了各种方法搞不定
后来换了vbs来处理,处理完再把结果作为参数传递给bat启动,搞定
作者: wskwfkbdn    时间: 2016-1-28 13:11

bat默认把运行参数使用空格符进行分割,即使能够把&进行转义处理,但包含空格的完整路径,将得不到完整的路径。唯一办法:%*接收所有参数,对参数进行文本处理。
作者: wskwfkbdn    时间: 2016-1-29 14:42

此题有解 纯P实现,以%*接收所有参数,然后使用for命令分割处理。
作者: CommandBatCmd    时间: 2017-2-1 22:37

多数时间是从技巧上寻找突破口,但只要把批处理的

预处理、变量扩展(%time%等)、延迟变量扩展(!time!等)、特殊字符(路径文件名与批处理共享的 ! % & ( ) , ; = ^ 等)

这四个核心概念放到一起,来分析楼主抛出的难题,感觉修改标题为【已解决】的可能性很小!




欢迎光临 批处理之家 (http://bathome.net./) Powered by Discuz! 7.2