Board logo

标题: [文件操作] 批处理命令dir 中 ? 的匹配问题 [打印本页]

作者: newswan    时间: 2024-2-1 12:33     标题: 批处理命令dir 中 ? 的匹配问题

??匹配的数量是 1 和 2
  1. dir /b /a-d *_??.pdf
复制代码
powershell 中 ?? 匹配数量是 2
  1. Get-ChildItem *_??.txt
复制代码
为什么 cmd 中是这样?
作者: Five66    时间: 2024-2-1 19:21

额,powershell中的问号(?)匹配一个字符,大概是powershell自己的规则
如果想要像cmd那样,试试用-Filter选项
Get-ChildItem -Filter *_??.txt
作者: 77七    时间: 2024-2-2 00:23

?匹配0个、1个,??匹配0个、1个、2个
作者: hnfeng    时间: 2024-2-2 10:50

本帖最后由 hnfeng 于 2024-2-2 11:03 编辑

那么,在cmd中,如何仅匹配:
0fca724925a0312d9261912bfe810eaa.bin
0fcbc1c2d20b8724f538718827a97d85.bin
0fe920300e68d25db45c915c0c755d73.bin
0ff4803731ace688f5ec1333ff40d569.bin
(32个 由0-9和a-f组成的字符串).bin

又不会误伤 正常的少于32个字符的普通 .bin 文件?
作者: qixiaobin0715    时间: 2024-2-2 11:03

回复 3# 77七
cmd还是有点奇葩,dir时,如果问号在前面看看会发生什么。
比如有这样一些文件:
abc123.txt
bc123.txt
c123.txt
123.txt
分别执行dir命令:
dir /b /a-d ???123.txt
dir /b /a-d ??123.txt
dir /b /a-d ?123.txt
结果是什么?
作者: Batcher    时间: 2024-2-2 11:29

回复 2# Five66


恰恰相反,正经的通配符:
? 匹配1个字符
* 匹配0个或1个或任意多个字符

反而是CMD自己的规则不符大多数软件和人的共识
作者: 77七    时间: 2024-2-2 13:47

本帖最后由 77七 于 2024-2-2 13:49 编辑

回复 5# qixiaobin0715


   
谢谢大佬指点!
经过测试发现,当一个?或者连续多个?出现在开头,且点号之前不全是?(出现其它字符),其它字符之前的?只匹配1个,其它字符之后的?匹配0个,1个。


  1. @echo off
  2. >123.txt cd.
  3. dir /b /a-d ????.txt
  4. echo=
  5. dir /b /a-d ??3.txt
  6. echo=
  7. dir /b /a-d ??3?.txt
  8. pause
复制代码


三个dir 命令全部匹配成功

另外发现问号"?"对点号 "."匹配失败
  1. @echo off
  2. >12.3.txt cd.
  3. dir /b /a-d ???3.txt
  4. echo=
  5. dir /b /a-d ??.3.txt
  6. pause
复制代码

作者: 77七    时间: 2024-2-2 13:52

回复 4# hnfeng


发现32个[0-9a-z]提示过长,所以用了两个findstr匹配
  1. dir /b /a-d |findstr /rixc:"................................\.bin" |findstr /rixc:"[0-9a-z][0-9a-z]*\.bin"
复制代码

作者: hnfeng    时间: 2024-2-2 16:37

回复 8# 77七


    不知道为什么,不成功。
  1. C:\temp\2.8Bin>dir /s /b /a-d
  2. C:\temp\2.8Bin\ShaderCache\0\00a356bb4dcffadfbecc027ff2f0f3f5.bin
  3. C:\temp\2.8Bin\ShaderCache\0\00abace58a961221e6c54cc17e90d1c3.bin
  4. C:\temp\2.8Bin\ShaderCache\0\00afde0f28ad42f7e08ce48672c2d6a8.bin
  5. C:\temp\2.8Bin\ShaderCache\a\a00e46701b2ebba310eb944fb78268a2.bin
  6. C:\temp\2.8Bin\ShaderCache\a\a0a2d4feb4186b3994675cb5cb6105c6.bin
  7. C:\temp\2.8Bin\ShaderCache\a\a0a67596c850febb29bde5f1c15e1839.bin
  8. C:\temp\2.8Bin\test\acddffdg hgfjhjjkjkj.bin
  9. C:\temp\2.8Bin\test\dfdgfdg hjhjkjk.bin
  10. C:\temp\2.8Bin\test\ffffffffff abcd.bin
  11. C:\temp\2.8Bin>dir /s /b /a-d |findstr /rixc:"................................\.bin" |findstr /rixc:"[0-9a-z][0-9a-z]*\.bin"
  12. C:\temp\2.8Bin>dir /s /b /a-d |findstr /rixc:"................................\.bin"
  13. C:\temp\2.8Bin>dir /s /b /a-d |findstr /rixc:"[0-9a-f][0-9a-f]*\.bin"
  14. C:\temp\2.8Bin>dir /s /b /a-d |findstr /rixc:"[0-9a-f]*\.bin"
  15. C:\temp\2.8Bin>dir /s /b /a-d |findstr /rixc:"[0-9a-f]*"
  16. C:\temp\2.8Bin>dir /s /b /a-d |findstr /rixc:"f.*"
  17. C:\temp\2.8Bin>dir /s /b /a-d |findstr /rixc:".*\.bin"
  18. C:\temp\2.8Bin\ShaderCache\0\00a356bb4dcffadfbecc027ff2f0f3f5.bin
  19. C:\temp\2.8Bin\ShaderCache\0\00abace58a961221e6c54cc17e90d1c3.bin
  20. C:\temp\2.8Bin\ShaderCache\0\00afde0f28ad42f7e08ce48672c2d6a8.bin
  21. C:\temp\2.8Bin\ShaderCache\a\a00e46701b2ebba310eb944fb78268a2.bin
  22. C:\temp\2.8Bin\ShaderCache\a\a0a2d4feb4186b3994675cb5cb6105c6.bin
  23. C:\temp\2.8Bin\ShaderCache\a\a0a67596c850febb29bde5f1c15e1839.bin
  24. C:\temp\2.8Bin\test\acddffdg hgfjhjjkjkj.bin
  25. C:\temp\2.8Bin\test\dfdgfdg hjhjkjk.bin
  26. C:\temp\2.8Bin\test\ffffffffff abcd.bin
  27. C:\temp\2.8Bin>
复制代码

作者: newswan    时间: 2024-2-2 16:56

dos 从开始,就有缺陷。
首先,抄 unix 的路径分隔符就行,非要反过来。
第二,换行符
第三,获取命令返回值

有谁比较了解这些历史,为什么 dos 不能按照 unix 的习惯。
作者: _avatar_    时间: 2024-2-2 17:20

回复 6# Batcher


    正则中的 ? 不是匹配 0 个或 1个吗?
作者: Batcher    时间: 2024-2-2 17:23

回复 11# _avatar_


    6楼说的是通配符,不是正则表达式。二者的区别你可以搜索一下。
作者: Batcher    时间: 2024-2-2 17:32

回复 9# hnfeng


你加了 /s 之后文件名前面增加了路径名,但是你的 findstr 命令使用了 /x 精确匹配,所以出错。改成这样试试:
  1. dir /s /b /a-d |findstr /r /i /c:"\\................................\.bin" | findstr /e /r /i /c:"\\[0-9a-f][0-9a-f]*\.bin"
复制代码

作者: hnfeng    时间: 2024-2-2 18:59

回复 13# Batcher


    谢谢,可以了
我原以为只要含有引号内所定义的字符就算匹配成功
另外,请教 "\\[0-9a-f][0-9a-f]*\.bin" 是不是与 "\\[0-9a-f]*\.bin" 相同效果?
作者: _avatar_    时间: 2024-2-2 21:10

回复 12# Batcher


    是我理解有误,我一直以为cmd中的通配符跟 正则中的量词是一样的。
作者: _avatar_    时间: 2024-2-2 21:18

回复  Batcher
请教 "\\[0-9a-f][0-9a-f]*\.bin" 是不是与 "\\[0-9a-f]*\.bin" 相同效果?
hnfeng 发表于 2024-2-2 18:59


并不相同哦,前一个 [0-9a-f] 是一定会有一个字符需要匹配,后面的 [0-9a-f]* 是匹配0个或多个。
也就是说 "\\[0-9a-f][0-9a-f]*\.bin" 可以匹配到一个字符的结果,也可以匹配到两个字符的结果,比如  d.bin  、  3.bin 、 af.bin  、 0c.bin ,但 "\\[0-9a-f]*\.bin" 就只能匹配一个字符的结果,比如 d.bin  、  3.bin
作者: hnfeng    时间: 2024-2-2 21:38

本帖最后由 hnfeng 于 2024-2-2 21:41 编辑

回复 16# _avatar_


    我怎么感觉是这样的:
[0-9a-f][0-9a-f]*  是 一个 [0-9a-f] 再跟0个或多个 [0-9a-f],结果可能是 a.bin, ab.bin,abcdef12345.bin,  abcdef12_345.bin .....
[0-9a-f]* 是 0个或多个 [0-9a-f],可以不含 [0-9a-f],结果可能是 上面那些,也可能是  zzzz.bin ...
作者: Five66    时间: 2024-2-2 22:11

回复 6# Batcher


    以cmd的这边来看,cmd的规则才是正经的,其他的都是不正经的
作者: WHY    时间: 2024-2-3 23:08

本帖最后由 WHY 于 2024-2-4 11:24 编辑

回复 8# 77七


    可以先用 findstr 确定全部由[a-f0-9]组成,再用字符偏移定位其长度,这样用1个findstr即可。
findstr 正则不支持表示范围的量词,只能选择折中方案。
举例:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for /f "delims=" %%i in ('dir /b /a-d *.bin ^| findstr /i "^[0-9a-f][0-9a-f]*\.bin$"') do (
  4.     set "name=%%~ni"
  5.     if "!name:~32!" == "" (
  6.         if "!name:~31!" NEQ "" echo;%%i
  7.     )
  8. )
  9. pause
复制代码

作者: 77七    时间: 2024-2-4 11:38

回复 19# WHY


   谢谢大佬指点!findstr 正则不支持指定重复次数,太遗憾了..
作者: WHY    时间: 2024-2-4 11:38

通过 findstr 管道套管道,能够解决4楼提出的具体问题,但并不通用,源于 findstr 不支持{m,n}这种表示范围的量词。

比如条件变一下:查找大于等于16个字符,小于等于32个字符,全部由[a-f0-9]组成的 .bin 文件。
这个时候如果仅仅用 findstr 来解,即使真能够实现,相信也是异常麻烦。
可以采用这种办法:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for /f "delims=" %%i in ('dir /b /a-d *.bin ^| findstr /i "^[0-9a-f][0-9a-f]*\.bin$"') do (
  4.     set "name=%%~ni"
  5.     if "!name:~32!" == "" (
  6.         if "!name:~15!" NEQ "" echo;%%i
  7.     )
  8. )
  9. pause
复制代码

作者: qixiaobin0715    时间: 2024-2-4 16:23

本帖最后由 qixiaobin0715 于 2024-2-5 09:21 编辑

使用findstr筛选确实麻烦:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for /l %%i in (1,1,32) do (
  4. set n1=!n1!.
  5. if %%i equ 16 set n2=!n1!
  6. )
  7. dir /b /a-d *.bin|findstr /i "^[0-9a-f][0-9a-f]*\.bin$"|findstr /v !n1!.\.bin|findstr !n2!\.bin
  8. pause
复制代码

作者: hnfeng    时间: 2024-2-5 08:35

多谢楼上各位,学习了学习了
作者: qixiaobin0715    时间: 2024-2-5 09:27

回复 14# hnfeng
唯一区别后者可以匹配到文件“.bin”(即文件名为空的bin文件),其它都一样。
作者: hnfeng    时间: 2024-2-5 10:12

经研究试验,发现 19楼的方法完美。

另外,我原以为 findstr /eric:"\\................................\.bin" 会列出文件名是 32位及不足32位的.bin文件(32个".", 1个"."代表任意一个字符), 今天意外发现 ,并不是全部不足32位文件名的文件都能列出来。
我产生了用1-50个1、1-50个a、a开头后面跟1-50个1的bin文件,结果发现,只有文件名位数是 15位,20位,27位,32位的文件才能被列出来,不知原因,高手们给解释一下?

下面是试验的命令:
  1. for /f "delims=" %i in ('dir /b /s /a-d *.bin ^| findstr /eric:"\\................................\.bin"') do @echo %i
复制代码

作者: qixiaobin0715    时间: 2024-2-5 10:25

本帖最后由 qixiaobin0715 于 2024-2-5 10:37 编辑

回复 25# hnfeng
代码中一系列的 . 在findstr中可以代表任意字符,包括路径中的 \ 。想一想就应当能够明白其中的问题了。
在你实验命令的路径中,下面命令应当能匹配15个字符的文件名(我是把连续的 . 中倒数第16个 . 替换成 \\ 而已):
  1. for /f "delims=" %i in ('dir /b /s /a-d *.bin ^| findstr /eric:"\\................\\...............\.bin"') do @echo %i
复制代码

作者: qixiaobin0715    时间: 2024-2-6 10:09

本帖最后由 qixiaobin0715 于 2024-2-6 14:40 编辑

用dir+findstr命令筛选文件名时最好不要带上路径字符,有时会带来不必要的麻烦,还是一步步的来比较简单明了,虽然代码行数会增加,但效率上不见得低多少:
  1. @echo off
  2. for /f "delims=" %%i in ('dir /s /b /ad') do (
  3.     if exist "%%i\*.bin" (
  4.         pushd "%%i"
  5.         for /f "delims=" %%j in ('dir /b /a-d^|findstr "................................\.bin"') do echo,%%~fj
  6.         popd
  7.     )
  8. )
  9. pause
复制代码

作者: WHY    时间: 2024-2-7 00:01

基于21楼的补充
1.查找大于等于32个字符、全部由[0-9a-f]组成、包括子目录里面的 .bin 文件:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for /f "delims=" %%i in ('dir /b /a-d /s *.bin ^| findstr /i "\\[0-9a-f][0-9a-f]*\.bin$"') do (
  4.     set "name=%%~ni"
  5.     if "!name:~31!" NEQ "" echo;%%i
  6. )
  7. pause
复制代码
2.查找小于等于32个字符、全部由[0-9a-f]组成、包括子目录里面的 .bin 文件:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for /f "delims=" %%i in ('dir /b /a-d /s *.bin ^| findstr /i "\\[0-9a-f][0-9a-f]*\.bin$"') do (
  4.     set "name=%%~ni"
  5.     if "!name:~32!" == "" echo;%%i
  6. )
  7. pause
复制代码
题外话:
网上看到过很多判断文件名长度的提问与回答,其中bat的回答基本上都是先计算字符串长度,再与给定数值进行比较。
其实没有必要,判断字符串长度与计算字符串长度不是一码事。
判断是事先给你一个已知数值,而计算肯定不会给你数值的。这是其一;
其二,CMD没有字符串长度计算的内部或外部命令,我认为即便是高效的二分法也不会比字符偏移法来的方便、直接。
作者: hnfeng    时间: 2024-2-7 14:26

回复 28# WHY


    赞一个,很棒的技巧
作者: newswan    时间: 2024-2-7 14:28

回复 28# WHY

记得有一个方法,用 set/a 计算字符串,把字符串 替换成 -1-1-1.... 这样的,效率高些
作者: _avatar_    时间: 2024-2-12 13:20

回复 17# hnfeng


    是我表达有误,而且有些武断,并没有做测试就想当然啦。
    当时想的是windows不能创建没有文件名,只有后缀的文件,所以自动把 .bin 这样的情况给略过了。
    本意就是想说两种写法结果会不一样。

    不过你的说法也有问题哦, zzzz.bin 是不会被匹配的,因为字母范围只到 f。




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