Board logo

标题: [文件操作] 可能是一个老问题,for 对带空格文件名的处理 [打印本页]

作者: DOSforever    时间: 2024-6-14 11:39     标题: 可能是一个老问题,for 对带空格文件名的处理

本帖最后由 DOSforever 于 2024-6-17 07:56 编辑

比如有若干个 *.txt 文件,其中有一个或多个带空格的文件名 filename with spaces.txt

for %f in (*.txt) do dir "%f" ,set 是通配符的时候引用 %f 必须加双引号,否则的话会提示找不到文件;
for %f in ("filename with spaces.txt") do dir %f ,set 不是通配符,只有单文件名的时候 %f 必须不加双引号,否则的话会提示找不到文件;
但如果
for %f in ("*.txt") do dir %f ,在 set 上加双引号是无效的,遇到通配符中有空格的文件名仍然会提示找不到文件;
for %f in ("filename with spaces.txt") do dir "%f" ,在 set 和 %f 上同时加双引号,遇到 set 是带空格的单一文件名是无效的,会提示找不到文件;

那么 set 中有时候是通配符,有时候是单文件名,也可能是多个通配符和单文件名在一起,比如
for %f in (*.doc "filename with space.txt" *.xls) do dir %f
那么后面的 %f 一会儿要加双引号,一会儿不加;那么有没有办法用一种统一的格式无论是通配符还是单一文件名对 %f 的引用都可以正确处理?

作者: 77七    时间: 2024-6-14 11:53

  1. dir "%~f"
复制代码

作者: Batcher    时间: 2024-6-14 13:08

回复 2# 77七


我喜欢使用 for /f 把杂七杂八的交给 dir 去处理,爱咋滴咋滴,反正我 for /f 格式固定
  1. @echo off
  2. for /f "delims=" %%i in ('dir /b /a-d *.doc "filename with space.txt" *.xls') do (
  3.     echo %%i
  4. )
  5. pause
复制代码

作者: aloha20200628    时间: 2024-6-14 13:15

本帖最后由 aloha20200628 于 2024-6-14 13:26 编辑

回复 1# DOSforever

一。用法逻辑的说明
   for %f in ("a * c.txt" "*&*.bat") do ...
   for /f "delims=" %f in (' dir /b "a * c.txt" "*&*.bat" ') do ...
以上两式等效,故择其一即可,不必在 set 中和 do ... 中复用同类功能
二。建议用双引号包裹每个文件名表达项
   for %f in ("a * c.txt" "*&*.bat") do echo,%f
   for %f in ("a * c.txt" "*&*.bat") do echo,%~f
以上两式的echo输出结果等效(即 %f 与 %~f 相同);关键是set中的每个文件名表达项,采用双引号包裹是为防止内含的空格或特殊字符如&被转义

作者: 77七    时间: 2024-6-14 14:39

回复 3# Batcher


   感谢站长分享!其实有时候揣摩楼主的意思挺难的,只要语法没错,我就当举了个例子,不去考虑是否符合实际了,或许楼主有特殊的用途。
作者: 77七    时间: 2024-6-14 15:18

本帖最后由 77七 于 2024-6-14 15:21 编辑
  1. for %%a in () do
复制代码
这种句式,我理解有两种用法,

如果 ()内的字符串含有通配符 *? ,则会匹配文件,这时候返回的结果不带引号。

如果 () 内不含通配符,就当作普通字符串处理,和目录下有无该文件没有关系。只不过如果字符串有特殊符号(包括这种句式的分隔符 空格、等号、分号、逗号、制表符,及批处理语法中其它特殊符号,比如& | ^ < >),需要用引号括起来,这时候返回的结果是带引号的。如果没有特殊符号,则加引号返回引号,不加则不返回引号。

如果 () 内既有 *.txt 又有  "1空格2.doc" ,返回的结果依照上述规则分别返回。

另外dir 的问题,大概是4种情况 dir ""1.txt"" 、dir ""1 2.txt"" 、dir 1 2.txt 、dir 12.txt
其中第一、二种 情况,大概和buyiyang大佬讲到的另一帖情况相似(http://www.bathome.net/redirect.php?goto=findpost&;ptid=68005&pid=281417),当 字符串没有空格时,多加一对引号没有影响,否则不行。
第三、四种情况,不必说了。
作者: DOSforever    时间: 2024-6-15 08:06

77七 发表于 2024-6-14 11:53

        试下来还是你这方法有效。要在 set 单文件名和 do 后面的 %f 两者都加上双引号,但要使用 %~f 这样的语法。也就是要用
for %f in (*.doc "filename with spaces.txt" *.xls) do dir/b "%~f" 这样的格式,
这样无论 set 中是通配符还是用双引号包裹起来的单一文件名都可以正确处理。

其他两位给出的方式不行,看上去是对的,但实际不行,因为用 echo 做测试有一定的局限性,echo 只能让你看看变量所获取的 token 是否是所期望的那样,但对这个 token 能否正确操作是另外一回事,因为我的操作是针对具体的文件,并不是无“实体”的字符串,所以光获取正确的 token 字符串还不行,还要看命令能否“正确”理解这个字符串,所以有一定要用 dir , ren , copy 等实际对文件操作的命令去验证。
作者: DOSforever    时间: 2024-6-15 08:44

本来对这个 %~f 语法还没注意,刚看到此说明的时候不知道是干什么的,现在想起来当年开发者专门设置了这样一个语法是不是就是为了解决 set 中有双引号引起的混乱问题?
作者: Ru_Evan    时间: 2024-6-16 18:40

回复 4# aloha20200628

这两运行大量文件时有区别吧?第一个内存占用会越来越多,第二个就没这问题。
作者: aloha20200628    时间: 2024-6-16 19:31

回复 9# Ru_Evan

用实测数据可验证采用 dir /b/s/a-d, for /r %%F in (*) 和 for /f "delims=" %%F in ('dir /b/s/a-d') 这三者获取文件列表的效能区别:
其一,搜索约有3万个文件的目录树并生成其全部文件列表的用时比较
        0.42s》dir /b/s/a-d>files.lst
        1.16s》(for /r %%F in (*) do echo,%%F)>files.lst
        22.6s》(for /f "delims=" %%F in ('dir /b/s/a-d') do echo,%%F)>files.lst
其二,for %%F in (*) 不能匹配隐藏文件,匹配表达式不能包含路径
当匹配文件数过千时,速度差别会愈趋明显,过万了就应采用第一方法,即使会产生临时文件也是值得的...

作者: newswan    时间: 2024-6-16 23:15

本帖最后由 newswan 于 2024-6-16 23:20 编辑

回复 10# aloha20200628

测试 powershell 呢
  1. $f = Get-ChildItem -Path "D:\share\" -Recurse
  2. $f.FullName | Out-File -FilePath  "a.txt" -Encoding 'utf8'
复制代码

作者: aloha20200628    时间: 2024-6-17 11:41

本帖最后由 aloha20200628 于 2024-6-17 11:42 编辑


在10楼原测试方案中加入powershell两个测试方法(均在cmd命令行运行多次后取稳定且最少用时结果),更新实测对比结果如下:
        搜索约有3万个文件的目录树并生成其全部文件列表的用时比较
        0.42s(1.0单位时)》dir /b/s/a-d>files.lst
        1.16s(2.8单位时)》(for /r %%F in (*) do echo,%%F)>files.lst
        19.1s(45.5单位时)》(for /f "delims=" %%F in ('dir /b/s/a-d') do echo,%%F)>files.lst
        2.03s(4.8单位时)》powershell "$s=(dir -r).fullname;sc files.lst $s"
        2.64s(6.3单位时)》powershell "(dir -r).fullname|sc files.lst"





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