在 for 循环中延迟扩展处理包含感叹号的路径

在 for 循环中延迟扩展处理包含感叹号的路径

我知道这里已经有几个关于这个问题的问题了,但我对批处理脚本还不熟悉,我发现这些解决方案很难应用于我的场景,或者直接不适用于我的脚本。
一个解决方案似乎是暂时禁用,DelayedExpansion但如果我这样做,那么什么都不起作用了。

下面的脚本试图完成什么,并且除了路径之外它都可以工作!,如下所示:

它将逐个获取源文件夹和文件(包括子文件夹),检查目标文件夹中是否存在该文件。如果文件不存在,则它可以执行两件事:

  • 如果文件是 png、jpg 或 mp4,则将其从源文件夹复制到目标文件夹
  • 如果目标文件夹不是.png.jpg.mp4,则在目标文件夹中创建一个虚拟文件(具有相同名称和扩展名的 0 字节文件)。我为此使用 xcopy(我复制一个现有的零字节文件并重命名它),因为这是我没有遇到特殊字符问题的唯一方法。
@echo off

setlocal EnableExtensions EnableDelayedExpansion 

rem Set the source and destination folders
set "src=L:\folderS\sub1\sub2"
set "dst=L:\folderD\sub1"

rem Set the path to the dummy file that we need to create dummy files with xcopy
set "dummy=L:\dummy.txt"

rem Create the destination folder if it doesn't exist
if not exist "%dst%" mkdir "%dst%"

rem Loop through all the files in the source folder
for /R "%src%" %%f in (*) do (

     rem Get the file path
     set "filePath=%%f"
     echo the full path is !filePath!

     rem Get the file extension
     set "ext=%%~xf"
     
     rem Get the relative path excluding the path until the specified src folder
     set "relPath=!filePath:%src%\=!"
     echo the relative path is !relPath!
     
     rem Check if the file does not exist in the destination
     if not exist "%dst%\!relPath!" (
     
         rem Check if the file extension is png jpg or mp4
         if /I "!ext!" == ".png" (
          
         rem If the file extension is png jpg or mp4 copy the file and display a message
         rem echo Copying "%%f" to "%dst%\!relPath!"
         xcopy /y "%%f" "%dst%\!relPath!*"
         
         )else if /I "!ext!" == ".jpg" (
             xcopy /y "%%f" "%dst%\!relPath!*"
         
         )else if /I "!ext!" == ".mp4" (
             xcopy /y "%%f" "%dst%\!relPath!*"
          
         )else (
                 
             rem If the file extension is not png jpg or mp4, create a dummy file
             echo Creating dummy "%%f" to "%dst%\!relPath!"
              
             xcopy /y "%dummy%" "%dst%\!relPath!*"
        )

    )
)
echo Done.

问题似乎是当我将文件的相对路径存储在 relPath 中时,如果文件名或任何子文件夹包含!那么它将忽略!但仍然以某种方式保留了文件扩展名。我认为,但我不确定,即使我设法在 relPath 中正确存储路径,稍后"%dst%\!relPath!"在 for 循环的其他部分中复制文件或检查文件是否存在时,我仍然会遇到问题。

我怎样才能解决这个问题?

编辑:这是一个屏幕截图,显示了一个非常简单的场景的结果: 在此处输入图片描述

编辑 2:我现在尝试了 @harrymc 的建议,似乎部分解决了问题,但一些文件名仍然有问题。新代码:

@echo off

setlocal EnableExtensions EnableDelayedExpansion 

rem Set the source and destination folders
set "src=L:\folderS\sub1\sub2"
set "dst=L:\folderD\sub1"

rem Set the path to the dummy file that we need to create dummy files with xcopy
set "dummy=L:\dummy.txt"

rem Create the destination folder if it doesn't exist
if not exist "%dst%" mkdir "%dst%"

rem Loop through all the files in the source folder
for /R "%src%" %%f in (*) do (

     rem Get the file path
     set "filePath=%%f"
     echo the full path is !filePath!

     rem Get the file extension
     set "ext=%%~xf"
     
     setlocal EnableExtensions EnableDelayedExpansion
     rem Get the relative path excluding the path until the specified src folder
     set "relPath=!filePath:%src%\=!"
     echo the relative path is !relPath!
     
     rem Check if the file does not exist in the destination
     if not exist "%dst%\!relPath!" (
     
         rem Check if the file extension is png jpg or mp4
         if /I "!ext!" == ".png" (
          
         rem If the file extension is png jpg or mp4 copy the file and display a message
         rem echo Copying "%%f" to "%dst%\!relPath!"
         xcopy /y "%%f" "%dst%\!relPath!*"
         
         )else if /I "!ext!" == ".jpg" (
             xcopy /y "%%f" "%dst%\!relPath!*"
         
         )else if /I "!ext!" == ".mp4" (
             xcopy /y "%%f" "%dst%\!relPath!*"
          
         )else (
                 
             rem If the file extension is not png jpg or mp4, create a dummy file
             echo Creating dummy "%%f" to "%dst%\!relPath!"
              
             xcopy /y "%dummy%" "%dst%\!relPath!*"
        )

    )
    endlocal
)
echo Done.

以下是仍存在问题的文件名示例。我认为这是由于文件名的长度或其他符号导致的 xcopy 限制。

在此处输入图片描述

编辑 3:现在已完全解决。剩下的最后一个问题是

xcopy /y "%%f" "%dst%\!relPath!*"

应该是:

echo F|xcopy "!filePath!" "%dst%\!relPath!"

这解决了我在某些符号方面仍遇到的问题,并且使用“F|”时,xcopy 不会每次都询问我它是否是文件。xcopy 可能仍有 256 个字符的限制,但我认为不会达到该限制。我可以使用 robocopy,效果会更好,但 robocopy 参数完全不同,我可能需要多次尝试才能正确完成。

谢谢大家!

答案1

根据帖子中的建议 通过延迟扩展来转义感叹号,我只附上了必要的部分里面使用命令的循环 setlocal,这对我的示例有效:

@echo off
set "src=C:\Temp\tmp9"
for /R "%src%" %%f in (*.txt) do (
     set "filePath=%%f"
     setlocal EnableExtensions EnableDelayedExpansion
     echo the full path is "!filePath!"
     set "relPath=!filePath:%src%\=!"
     echo relative path is "!relPath!"
     endlocal
)

得到了这个结果:

在此处输入图片描述

答案2

BAT文件

@echo off

setlocal EnableExtensions EnableDelayedExpansion 

rem Set the source and destination folders
set "src=L:\folderS\sub1\sub2"
set "dst=L:\folderD\sub1"

rem Set the path to the dummy file
set "dummy=L:\dummy.txt"

rem Set list of extensions to copy
set "ext=.png .jpg .mp4"

rem loop through all files in source folder and subfolders
for /f "tokens=* delims= " %%f in ('Dir "%src%" /A:-D /b /s') do (
  
  rem set default dummy flag state (on)
  set "dummyFlag=on"
  
  rem set filepath
  set "filePath=%%f"
  
  rem Get the path to parent relative to %src%
  set "relPath=!filePath:%src%=!"
   
  rem continue if destination file does not exist  
  if not exist "%dst%!relPath!" (
  
    rem compare list of extensions to current file extension
    for %%e in (%ext%) do (
    
      rem if current file extension matches extension from list      
      if "%%~xf" == "%%e" (
      
        xcopy /-I /Q "!filePath!" "%dst%!relPath!*" > nul
        set "dummyFlag=off"
       
      )
       
    )
  
    rem check if dummyFlag is still set 
    if "!dummyFlag!" == "on" (
    
      rem Copy dummy file to destination
      xcopy /-I /Q "%dummy%" "%dst%!relPath!*" > nul

    )

  )

) 

 

笔记

  • if not exist "%dst%" mkdir "%dst%"没有必要,因为xcopy将根据需要创建目录

  • 添加了扩展列表,以简化添加和删除扩展并减少代码重复set "ext=.png .jpg .mp4"

  • 使用dir "%src%" /A:-D /b /sfor 循环的方法(“%src%”及其子文件夹中的所有(仅)文件的列表

  • 添加了一个标志,用于确定文件是否与某个扩展名匹配!dummyFlag!=<on/off>

  • 修改的xcopy

    • 删除了/y开关,因为没有文件应该覆盖,否则为什么要检查if not exist
    • 添加开关/-I,如果目标不存在,则将其理解为文件
    • 添加开关/Q不列出要复制的文件名
    • 添加> NULL以抑制完成消息

答案3

*/ 编辑

我尝试了这个但出现了错误:“完整路径太长。 完毕。”
最长的路径是:L:\folderS\sub1\sub2\Another Test with Even more Symbols and long Name - And also, commas and brakets! (w23) (We,gasg) adsfasdfdas-asdf.png这确实很长,但仍然完全受 Windows 支持。

在 Windows API 中(以下段落中讨论了某些例外情况),路径的最大长度为 MAX_PATH,其定义为260 个字符
本地路径的结构如下: >驱动器号、冒号、反斜杠、由反斜杠分隔的名称组件以及终止空字符。
例如,驱动器上的最大路径D"D:\some 256-character path string<NUL>"其中"<NUL>"表示当前系统代码页的不可见终止 ^null` 字符。

观察:此处使用字符 < > 是为了视觉清晰,不能作为有效路径字符串的一部分。


我的系统已经启用了对长度超过 260 个字符的路径的支持,现在如果您的路径超过 260 个字符,那么我建议您检查本版本中链接的信息并将其与您系统上的设置进行比较,希望能解决这个新事件。

请注意,我对长路径/名称没有任何问题,我甚至添加了屏幕截图:

在此处输入图片描述


  • 要从命令行检查是否
    已启用长名称支持,0x1或已禁用0x0(默认),尝试:
E:\>reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem" /f "LongPathsEnabled"

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem
   LongPathsEnabled    REG_DWORD    0x1

  • 启用 (0x1)或禁用(0x0)命令行上对长路径名的支持,请尝试:
:: To enable support for long path names ::
> reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem" /v "LongPathsEnabled" /t REG_DWORD /d 1 /f

:: To  disable support for long path names ::
> reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem" /v "LongPathsEnabled" /t REG_DWORD /d 0 /f



@echo off 

setlocal EnableExtensions DisableDelayedExpansion
     
set "_src=L:\folderS\sub1\sub2"
set "_dst=L:\folderD\sub1"

robocopy "%_src%" "%_dst%" /e /xf *.* /copy:d | findstr /e \\

for /R "%_src%" %%G in (*) do (
     
     set "_var=%%~fG"
     
     setlocal EnableDelayedExpansion
     
     call set "_filePath=!_var:^!=^^!!"
     <con: call set "_rltvPath=%%_filePath:!_src!\=%%"
     <con: call set "_dummyFile=!_rltvPath:%%~xG=.txt!"
     
     call echo\the file path is "!_filePath!"
     call echo\the relative path is "!_rltvPath!"
     call echo\the dummy path file is "!_dummyFile!"
     
     if not exist "%_dst%\!_rltvPath!" (
     
         if /I "%%~xG" == ".png" (
            
             call copy /y "!_filePath!" "%_dst%\!_rltvPath!"
                 
            )else if /I "%%~xG" == ".jpg" (
             
             call copy /y "!_filePath!" "%_dst%\!_rltvPath!"
            
            )else if /I "%%~xG" == ".mp4" (
            
             call copy /y "!_filePath!" "%_dst%\!_rltvPath!"
             
            )else (
             
             call echo\cd.>"%_dst%\!_dummyFile!"
            
            )
            
        )
     
     echo\ & endlocal
     
    )

echo Done. & endlocal 

主要方法是,首先定义变量,然后在 new 中使用转义符开启延迟扩展子字符串对于转义变量,然后在使用时使用call command以便实时延迟逃脱发挥了作用

答案4

添加这样的东西可以解决问题吗?

set filePath=%filePath:!=^^^^!%

我们将查找!路径中的所有字符,并用 替换它们,以^!消除导致您遇到这些麻烦的特殊字符。如有必要,您可以尝试改变前面!的 数量。^!

相关内容