【问题标题】:Escaping ampersand from an external variable in a Batch file从批处理文件中的外部变量中转义与符号
【发布时间】:2021-03-06 11:36:39
【问题描述】:

我想让脚本做什么:

我需要通过删除 Handbrake 添加的 -1 后缀来重命名 Handbrake 创建的 .mkv 视频文件,然后删除具有完全相同名称(减去 -1)的原始源视频文件,但也可能是不同的文件扩展名。

一些注意事项:

  • 我正在使用 Windows SendTo 将源文件作为外部变量传递给脚本
  • 源视频文件和文件目录可能包含空格、& 和括号
  • 源文件可能具有不同的扩展名,例如 .mp4、.mkv、.wmv、.mov
  • 文件的目录位置不固定
  • 批处理脚本不会与文件在同一目录中执行
  • 使用NirCmd的clonefiletime命令将原文件的时间属性复制到新文件中
  • 使用 NirCmd 的 moverecyclebin 命令将文件移动到回收站而不是使用 del 命令

示例和预期行为

Directory: C:\Test test\

Files:
Test File & final (test)-1.mkv - Handbrake created file I want to rename
Test File & final (test).mp4 - original source file I want to delete
Test File & final.mp4
Test File.mp4
Otherfile.mp4
Another File.wmv
DifferentFile (1234).mkv

在这种情况下,Test File & final (test).mp4 的时间属性被复制到Test File & final (test)-1.mkv,然后Test File & final (test).mp4 被移动到回收站,最后Test File & final (test)-1.mkv 被重命名为Test File & final (test).mkv。

我目前拥有的代码:

@ECHO Off
SETLOCAL EnableDelayedExpansion
SET source=%1
FOR %%z IN (!source!) DO (
  SET fpath=%%~dpz
  SET filename=%%~nz
  SET ext=%%~xz
  SET fileprune=!filename:~0,-2!
  )
  (
    ENDLOCAL
    SET "fpath=%fpath%"
    SET "filename=%filename%"
    SET "fileprune=%fileprune%"
    SET "ext=%ext%"
    SET "fixed=%fileprune%%ext%"
    
)
FOR /f "delims=" %%a IN ('dir /b "%fpath%" ^| findstr /L /E /C:"%fileprune%.mp4" /C:"%fileprune%.wmv" /C:"%fileprune%.mkv" /C:"%fileprune%.mov"') DO (
  REM use NirCmd to copy the timestamp of the original file over to new file
  nircmdc clonefiletime "%fpath%%%a" %source%
  REM use NirCmd to move old file to recycle bin instead of del command as a failsafe measure
  nircmdc moverecyclebin "%fpath%%%a"
)
FOR %%b IN ("%fixed%") DO (
  REN %source% "%%~nxb"
)
EXIT

问题:

脚本对于包含与号的文件名失败,我认为空格存在问题,因为 Windows 会自动在文件路径和包含它们的名称周围添加引号。

例如,"C:\Test test\Test File & final (test)-1.mkv" 在使用 Windows SendTo 时被自动引用,而我用双引号将整个变量括起来会解决 & 号问题,但它会变成 SET "source="C:\Test test\Test File & final (test)-1.mkv"",这也会破坏脚本。

我也尝试过 SET "source=%~1",但它会破坏第一个 FOR 循环 - 下面的示例输出:

C:\Test test>(
SET fpath=C:\
 SET filename=Test
 SET ext=
 SET fileprune=!filename:~0,-2!
)

C:\Test test>(
SET fpath=C:\Test test\test\
 SET filename=Test
 SET ext=
 SET fileprune=!filename:~0,-2!
)

C:\Test test>(
SET fpath=C:\Test test\
 SET filename=File
 SET ext=
 SET fileprune=!filename:~0,-2!
)

C:\Test test>(
SET fpath=C:\Test test\
 SET filename=&
 SET ext=
 SET fileprune=!filename:~0,-2!
)

C:\Test test>(
SET fpath=C:\Test test\
 SET filename=final
 SET ext=
 SET fileprune=!filename:~0,-2!
)

C:\Test test>(
SET fpath=C:\Test test\
 SET filename=(test)-1
 SET ext=.mkv
 SET fileprune=!filename:~0,-2!
)

C:\Test test>(
ENDLOCAL
 SET "fpath=C:\Test test\"
 SET "filename=(test)-1"
 SET "fileprune=(test)"
 SET "ext=.mkv"
 SET "fixed=(test).mkv"
)

代码很乱,因为我对编码一无所知,但我很感激任何帮助。

【问题讨论】:

  • 使用set "source=%~1"删除潜在的引号,然后使用for %%z in ("!source!") do (…);因为你从不使用普通的%%z,但总是使用一些~-修饰符(如%%~nz),此时引号再次被删除......
  • 感谢 aschipfl,这实际上是一个相对简单的修复。

标签: windows batch-file cmd rename


【解决方案1】:

通过双引号所有变量的定义,以及环境属性的操作,麻烦的字符可以在 for 循环中处理。

通过在禁用延迟扩展的环境中为变量分配初始值,可以保留麻烦的字符,包括!

然后可以启用延迟扩展以安全地执行子字符串修改,而不管毒字符如何。

一个例子:

@Echo off & CD "%~dp0" 
 Setlocal DisableDelayedexpansion
 Set "demofile=test!^!&~.txt"
 Break >"%demofile%"
 For %%G in ("test!*.txt")Do (
  Set "Filepath=%%~nG"
  Call :Sub Filepath
 )
 Set Filepath
 del "%demofile%"
Goto :Eof
:sub
 Set /A i+=1 + 0
 Setlocal EnableDelayedexpansion
 Set "_tmp=!%1!"
 Set "_tmp=!_tmp:&=^&!"
 Endlocal & Set "%1[%i%]=%_tmp%"
 Set %1=
Goto :Eof

【讨论】:

  • 方法被修改以防止在调用 sub 时插入符号加倍。
  • 很好,但您可以使用set "_tmp=!%1!" 代替Call Set "_tmp=%%%1%%"
  • 你确实是对的,想太多事情的乐趣,我推测!会在没有实际测试的情况下尝试在延迟扩展环境中扩展。
【解决方案2】:

感谢 aschipfl 和 T3RR0R 指出我在某些变量周围缺少双引号。

修改后的代码:

@ECHO Off
SETLOCAL EnableDelayedExpansion
SET "source=%~1"
FOR %%z IN ("!source!") DO (
  SET nsource=%%~dpnxz
  SET fpath=%%~dpz
  SET filename=%%~nz
  SET ext=%%~xz
  SET fileprune=!filename:~0,-2!
  )
  (
    ENDLOCAL
    SET "nsource=%nsource%"
    SET "fpath=%fpath%"
    SET "filename=%filename%"
    SET "fileprune=%fileprune%"
    SET "ext=%ext%"
    SET "fixed=%fileprune%%ext%"
    
)
FOR /f "delims=" %%a IN ('dir /b "%fpath%" ^| findstr /L /E /C:"%fileprune%.mp4" /C:"%fileprune%.wmv" /C:"%fileprune%.mkv" /C:"%fileprune%.mov"') DO (
  REM use NirCmd to copy the timestamp of the original file over to new file
  nircmdc clonefiletime "%fpath%%%a" "%nsource%"
  REM use NirCmd to move old file to recycle bin instead of del command as a failsafe measure
  nircmdc moverecyclebin "%fpath%%%a"
)
FOR %%b IN ("%fixed%") DO (
  REN "%nsource%" "%%~nxb"
)
EXIT

【讨论】:

    猜你喜欢
    • 2010-11-22
    • 2011-02-15
    • 1970-01-01
    • 2022-06-15
    • 1970-01-01
    • 1970-01-01
    • 2017-08-16
    • 2011-04-20
    相关资源
    最近更新 更多