crackerbear
crackerbear

Reputation: 158

Escaping ampersand from an external variable in a Batch file

What I want the script to do:

I need to rename a .mkv video file created by Handbrake by removing the -1 suffix, which Handbrake adds, then delete the original source video file which has the exact same name (minus the -1), but also potentially a different file extension.

Some caveats:

Example and expected behaviour

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

In this scenario, the time attributes of Test File & final (test).mp4 are copied over to Test File & final (test)-1.mkv, Test File & final (test).mp4 is then moved to the recycle bin and finally Test File & final (test)-1.mkv is renamed to Test File & final (test).mkv.

Code I have so far:

@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

The problem:

The script fails for filenames which contain ampersands and I believe there's an issue with spaces, in that Windows automatically adds quotes around the file path and name which contains them.

For example "C:\Test test\Test File & final (test)-1.mkv" is quoted automatically when using Windows SendTo and I read surrounding the entire variable with double quotes would resolve the ampersand issue, but it would become SET "source="C:\Test test\Test File & final (test)-1.mkv"" which also breaks the script.

I've also tried doing SET "source=%~1", but it then breaks the first FOR loop - example output below:

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"
)

The code is a mess since I know nothing about coding, but I'd appreciate any help.

Upvotes: 0

Views: 436

Answers (2)

T3RR0R
T3RR0R

Reputation: 2951

by doublequoting the definition of all variables, and manipulation of the environment properties troublesome characters can be processed within a for loop.

by assigning the initial value to the variable in an environment where delayed expansion is disabled, troublesome characters can be preserved including !

Delayed expansion can then be enabled to safely perform substring modification regardless of poison characters.

an example:

@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

Upvotes: 2

crackerbear
crackerbear

Reputation: 158

Thanks to aschipfl and T3RR0R for pointing out I was missing double quotes around some variables.

Revised code:

@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

Upvotes: 0

Related Questions