1

This is the output of my two scripts. The first one calls the other and delivers a filename as %1

D:\VIDEOS\SHORTS>for %v in (*) do call namer "%v"

D:\VIDEOS\SHORTS>call namer "10 Products You’ll Never Buy Again Knowing How They Are Made!.mp4"

D:\VIDEOS\SHORTS>echo "10 Products You’ll Never Buy Again Knowing How They Are Made.mp4"   | find /i ".tmp"   1>nul  && exit /b

The echo uses "%~nx1".

My question is: Why does the exclamation mark disappear?

This started happening yesterday, but I'm not sure what I could've done to cause this. It's part of a massive batch script cluster, all of which I can't post here due to size limits so ask for additional details or generalize.

edit: requested file namer.cmd

set "full=" & set "file=" & set "name=" & set "type=" & set "year=" & set "episode=" & set "strict=" & set "loose="
echo "%~nx1" | find /i ".tmp" >nul && exit /b
set "file=%~n1"

:: =============================================================================================================================
::      1. file VALIDATOR
:: =============================================================================================================================

set "original=%file%"
set "file=%file:!=%"
set "file=%file:#=%"
set "file=%file: _ = - %"
set "file=%file:’='%"
set "file=%file:“='%"
set "file=%file:”='%"
set "file=%file:[=(%"
set "file=%file:]=)%"
set "file=%file:_= %"
set "file=%file:—=-%"
set "file=%file:  = %"
set "file=%file:  = %"
set "file=%file:&=and%"

:: exclamation point has to be removed before we enable delayed expansion
if not "%file%"=="%original%" ren "%~1" "%file%%~x1" 2>nul && echo validated "%file%%~x1"
if not "%file%"=="%original%" if exist "%original%" ren "%~1" "%file% - copy%~x1" && echo validated "%file% - copy%~x1"
if not "%file%"=="%original%" if exist "%original%" ren "%~1" "%file% - copy 2%~x1" && echo validated "%file% - copy 2%~x1"

setlocal enabledelayedexpansion
set "original=!file!"
set "file=!file:%%=_percent!"
if not "%file%"=="%original%" ren "%~dp1%original%%~x1" "for /f "delims=" %%v in ('where /r "d:\videos" "%strict%*" 2^>nul') do" && echo validated "%original%"

set "original=!file!"
:: this needs to separated in order to add s01 tag if no season tag present
for /l %%s in (0,1,9) do set "file=!file:series.%%s.=S0%%s!"
for /l %%e in (0,1,9) do set "file=!file:%%eof2=E0%%e!"
for /l %%e in (0,1,9) do set "file=!file:%%eof3=E0%%e!"
for /l %%e in (0,1,9) do set "file=!file:%%eof4=E0%%e!"
for /l %%e in (0,1,9) do set "file=!file:%%eof5=E0%%e!"
for /l %%e in (0,1,9) do set "file=!file:%%eof6=E0%%e!"
for /l %%e in (0,1,9) do set "file=!file:%%eof7=E0%%e!"
for /l %%e in (0,1,9) do set "file=!file:%%eof8=E0%%e!"
for /l %%e in (0,1,9) do set "file=!file:%%eof9=E0%%e!"
:: if file has changed but no season tag present add s01
if not "%file%"=="%original%" echo "%file%" | find /i "s0" >nul || set "file=!file:E0=S01E0!"
if not "%file%"=="%original%" ren "%~dp1%original%%~x1" "%file%%~x1" && echo validated "%original%"

:: =============================================================================================================================
::      2. EXTRAPOLATOR
:: =============================================================================================================================

:extrapolate
set "type=" & set "year=" & set "ss=" & set "ep=" & set "episode="
set "name=%file%"
echo "%name%"| find " " >nul || set "name=%name:.= %" & rem if no spaces then then safer to replace dots

:: if short do complete skip of movie/series extrapolator breakdown
echo "%~n1" | find /i "trailer" >nul && set "type=misc" && goto skipyear
echo "%~p1" | find /i "\shorts\" >nul && set "type=misc" && goto skipyear
echo "%~p1" | find /i "\movies\" >nul && set "type=film" && goto skipss
echo "%~p1" | find /i "\series\" >nul && set "type=show"

:: episode number: last tag first
FOR /L %%e IN (100,1,199) DO (
 SET "modname=!name:E%%e=!"
 IF "!modname!" neq "%name%" (
  SET "setcmd=set name=!name:E%%e=& set ep=E%%e& set type=show& rem !"
  GOTO gotep
 )
)
FOR /L %%e IN (10,1,99) DO (
 SET "modname=!name:E%%e=!"
 IF "!modname!" neq "%name%" (
  SET "setcmd=set name=!name:E%%e=& set ep=E%%e& set type=show& rem !"
  GOTO gotep
 )
 SET "modname=!name:E0%%e=!"
 IF "!modname!" neq "%name%" (
  SET "setcmd=set name=!name:E0%%e=& set ep=E0%%e& set type=show& rem !"
  GOTO gotep
 )
)

FOR /L %%e IN (0,1,9) DO (
 SET "modname=!name:E0%%e=!"
 IF "!modname!" neq "%name%" (
  SET "setcmd=set name=!name:E0%%e=& set ep=E0%%e& set type=show& rem !"
  GOTO gotep
 )
 SET "modname=!name:E00%%e=!"
 IF "!modname!" neq "%name%" (
  SET "setcmd=set name=!name:E00%%e=& set ep=E00%%e& set type=show& rem !"
  GOTO gotep
 )
 SET "modname=!name:%%eof=!"
 IF "!modname!" neq "%name%" (
  SET "setcmd=set name=!name:%%eof=& set ep=E0%%e& set type=show& rem !"
  GOTO gotep
 )
 SET "modname=!name:x0%%e=!" 
 IF "!modname!" neq "%name%" (
  SET "setcmd=set name=!name:x0%%e=& set ep=E0%%e& set type=show& rem !"
  GOTO gotep
 )
)

GOTO skipep
:gotep
%setcmd%
:skipep

:: season number: second to last tag second
FOR /L %%e IN (0,1,9) DO (
 SET "modname=!name:S0%%e=!"
 IF "!modname!" neq "%name%" (
  SET "setcmd=set name=!name:S0%%e=& set ss=S0%%e& set type=show& rem !"
  GOTO gotss
 )
)
FOR /L %%e IN (10,1,99) DO (
 SET "modname=!name:S%%e=!"
 IF "!modname!" neq "%name%" (
  SET "setcmd=set name=!name:S%%e=& set ss=S%%e& set type=show& rem !"
  GOTO gotss
 )
)
FOR /L %%e IN (0,1,9) DO (
 SET "modname=!name:series.%%e=!"
 IF "!modname!" neq "%name%" (
  SET "setcmd=set name=!name:series.%%e=& set ss=S0%%e& set type=show& rem !"
  GOTO gotss
 )
)

GOTO skipss
:gotss
%setcmd%
:skipss

if defined ep if not defined ss set ss=S01
set "episode=%ss%%ep%"

:: year number: first tag last (first with brackets so not to confuse with imdb tag)
FOR /L %%e IN (1960,1,2020) DO (
 SET "modname=!name:(%%e)=!"
 IF "!modname!" neq "%name%" (
  SET "setcmd=set name=!name:(%%e)=& set year=%%e& if not defined type set type=film& rem !"
  GOTO gotyear
 )
)
FOR /L %%e IN (1960,1,2020) DO (
 SET "modname=!name:%%e=!"
 IF "!modname!" neq "%name%" (
  SET "setcmd=set name=!name:%%e=& set year=%%e& if not defined type set type=film& rem !"
  GOTO gotyear
 )
)
GOTO skipyear
:gotyear
%setcmd%
:skipyear

if not defined type set type=misc

:: other trimming for misc etc
set name=%name: - copy=&rem %
set name=%name: (1)=&rem %
set name=%name: (2)=&rem %
set name=%name: (3)=&rem %
set name=%name: [1]=&rem %
set name=%name: [2]=&rem %
set name=%name:360p=&rem %
set name=%name:480p=&rem %
set name=%name:720p=&rem %
set name=%name:1080p=&rem %
set name=%name:2160p=&rem %

:: changes for general use
set "name=%name: - = %"
set "name=%name:  = %"

:: trim last useless letter from name
set "name=%name%endtrimmer"
set "name=%name:(endtrimmer=endtrimmer%"
set "name=%name:[endtrimmer=endtrimmer%"
set "name=%name:.endtrimmer=endtrimmer%"
set "name=%name:-endtrimmer=endtrimmer%"
set "name=%name: endtrimmer=endtrimmer%"
set "name=%name: endtrimmer=%"
set "name=%name:_endtrimmer=%"
set "name=%name:endtrimmer=%"

:: set strings
if "%type%"=="misc" set "strict=%name: =?%"
if "%type%"=="film" if defined year set "strict=%name: =?%?(%year%)"
:: caution: this allows movie to be renamed without year tag
:: if "%type%"=="film" if not defined year set "strict=%name: =?%"
if "%type%"=="show" if defined year set "strict=%name: =?%?(%year%)?%episode%"
if "%type%"=="show" if not defined year set "strict=%name: =?%?%episode%"
if "%type%"=="show" if defined year set "loose=%name: =?%?(%year%)?s"
if "%type%"=="show" if not defined year set "loose=%name: =?%?s"

FOR %%a IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET "name=!name:%%a=%%a!"
endlocal & set "full=%~dp1%file%%~x1" & set "file=%file%%~x1" & set "name=%name%" & set "type=%type%" & set "year=%year%" & set "episode=%ss%%ep%" & set "strict=%strict%" & set "loose=%loose%"

:: declare (disable never delete)
echo extrapolated %type% "%strict:?= %"

:: =============================================================================================================================
::      3. FILEBOT MODERATOR
:: =============================================================================================================================

:: if rename not needed exit
if "%type%"=="misc" exit /b
if "%~2"=="-norename" exit /b

:: if already renamed we are done here
echo "%file%" | find /i " tt" >nul && exit /b

:: prep fake temp file
set tempdir=%temp%\%date:~3,2%%date:~6,2%%date:~11,2%%time:~1,1%%time:~3,2%%time:~6,2%%time:~9,2%
mkdir "%tempdir%" && copy /y nul "%tempdir%\%file%" >nul

:: variables (to shorten lines)
if "%type%"=="film" set cf=--db TheMovieDb --format "{n} ({y}) {director} {genres} r{rating} {vf} {vc} {ac} {imdbid}"
if "%type%"=="show" set cf=--db TheTVDb --format "{n} {s00e00} {t} {vf} {vc} {ac}-mallSanta"

:: rename fake temp file
filebot -rename "%tempdir%\%file%" --q "%name% %year%" %cf% >nul 2>nul

:: new name into variable
for /r "%tempdir%" %%i in (*) do set "new=%%~nxi"

:: declare failure
if "%file%"=="%new%" echo failed, reason: && filebot -rename "%tempdir%\%file%" --log warning --q "%name% %year%" %cf% & exit /b

:: check all strings against renamed orphan fake file and if successful rename 4real
for /f "delims=" %%v in ('where "%tempdir%:%strict%*" 2^>nul') do goto 4real
for /f "delims=" %%v in ('where "%tempdir%:%strict:s?='s?%*" 2^>nul') do goto 4real
for /f "delims=" %%v in ('where "%tempdir%:%strict:?-?=?%*" 2^>nul') do goto 4real

:: declare failure and confirm override
echo.
echo file "%file%" was renamed
echo to a "%new%", which was unexpected,
:: try the interactive command prompt mode in filebot?
choice /t 20 /c yn /n /d n /m "override and approve this rename? [Y]es or [N]o: "
if "%errorlevel%"=="1" goto 4real
exit /b

:4real
filebot -rename "%~dp1%file%" --q "%name% %year%" %cf% >nul 2>nul
if not exist "%~dp1%file%" echo successfully renamed "%file%"

:: validate the new file (maybe just send to validate with a -norename? takes longer but clearer code
set "original=%file%"
set "file=%file:!=%"
set "file=%file:#=%"
set "file=%file: _ = - %"
set "file=%file:’='%"
set "file=%file:“='%"
set "file=%file:”='%"
set "file=%file:_= %"
set "file=%file:—=-%"
set "file=%file:  = %"
set "file=%file:  = %"
set "file=%file:&=and%"
if not "%file%"=="%original%" ren "%~1" "%file%" && echo validated "%original%"
setlocal enabledelayedexpansion
set "original=!file!"
set "file=!file:%%= percent!"
if not "%file%"=="%original%" ren "%~dp1%original%%~x1" "%file%" && echo validated "%original%"
endlocal
exit /b

it (1.) removes problematic characters from the filename (2.) from filename extrapolates video name, year, other variables, then (3.) does a supervised filebot rename. the setlocal and endlocal bits look good. could it be the problem is inherited from yet another script?

Bricktop
  • 533
  • 3
  • 22
  • How about using `^!` when delayedexpansion is off? –  Aug 31 '17 at 05:41
  • you can use `^!` with delayedexpansion off or just use `^^!` when using `enable delayedexpansion` unless the ! is inside quotes, then still you only need to use `^!` – Gerhard Aug 31 '17 at 08:47
  • how will this `^!` help when it's in the unknown filename? seems like fixing the issue is better than hacking through a mediary variable – Bricktop Sep 01 '17 at 10:46

2 Answers2

3

You have enabled delay environment variable expansion with setlocal EnableDelayedExpansion somewhere above the ECHO line in batch script namer.

Therefore the exclamation mark is not further interpreted as literal character, but as begin/end of a variable reference expanded delayed.

We would need the batch code of namer batch file to help you solving this issue.

It looks like the line is for checking if the file contains .tmp, i.e. has the file extension .tmp and if this is the case, ignore the file.

I suggest to change in your first batch file

for %%v in (*) do call namer "%%v"

to

for %%I in (*) do if /I not "%%~xI" == ".tmp" call "%~dp0namer.cmd" "%%I"

Then files with file extension .tmp are not processed at all by namer batch file.

Run in a command prompt window set /? for more information about delayed expansion and for /? for help on %~xI (file extension of file) as well as if /? for help on command IF and call /? for help on %~dp0.

It is also possible to use in namer.cmd:

if /I "%~x1" == ".tmp" goto :EOF

This exits processing of the batch file namer.cmd if the file extension of first argument is .tmp (compared case-insensitive). For this comparison it does not matter if delayed expansion is enabled or not. See also answer on Where does GOTO :EOF return to?


If .tmp is not the file extension of the file, but is just somewhere within file name, it is possible to use an IF with string substitution.

Example for batch file namer.cmd.

set "FileName=%~nx1"
if not "%FileName:.tmp=%" == "%FileName%" goto :EOF

The first line assigns file name with extension but without path to environment variable FileName.

The second line compares case-sensitive the file name string in which all occurrences of .tmp are replaced case-insensitive with an empty string with the file name string not modified at all. The two compared strings are not equal if the file name string really contains .tmp 1 or more times resulting in exiting processing of the batch file.

In other words the IF condition checks if a string assigned to an environment variable contains case-insensitive the string specified between : and =.

That is similar to using in C:

#include <string.h>

int main(int argc, char *argv[])
{
    if(argc > 1)
    {
        /* Does first argument contain the string ".tmp"? */
        if(strstr(argv[1],".tmp") != NULL) return 1;
    }
    return 0;
}

Function strstr is case-sensitive.

Mofi
  • 46,139
  • 17
  • 80
  • 143
  • actually the .tmp is part of the full filename not only extension. this is to prevent temporary YTD files from being processed. it uses `.tmp` extension for the tracks while downloading and then combines them into `*.tmp.mp4`. namer.cmd added. – Bricktop Sep 01 '17 at 10:52
  • is there any way to check if delayed expanion is enabled or not? – Bricktop Sep 02 '17 at 09:34
  • 1
    @bricktop Yes, for example with `if "!" == "" echo Delayed expansion is enabled.` The __IF__ condition is only true if delayed expansion is enabled, otherwise the two compared strings are never equal. – Mofi Sep 02 '17 at 10:15
  • I started a major overhaul on all 11 script in this cluster to get rid of these `setlocal` issues I am now having an issue where the variables namer.cmd extrapolates are not carried over to none of the four scripts that do the requests. I broke the script down to three separate scripts and made sure `endlocal` is not used anywhere, only `disabledelayedexpansion` but the variables still disappear when returning to the script that requested them. I feel like I'm taking crazy pills. – Bricktop Sep 02 '17 at 15:16
  • 1
    @bricktop Help output on running in a command prompt window `setlocal /?` explains: *When the end of a batch file script is reached, an implicit ENDLOCAL is executed for any outstanding SETLOCAL commands issued by that batch script.* So your set of batch files should not make use of `setlocal` wherever possible and pass environment variables over local environment boundary. For example after `set "file=!file:%%=_percent!"` in `namer.cmd` use `endlocal & set "file=%file%"` to set `file` in restored environment to value of `file` in discarded environment. – Mofi Sep 02 '17 at 15:31
  • thanks, I thought I could use `setlocal disabledelayedexpansion` instead of `endlocal` to quit the environment but keep the variables. – Bricktop Sep 02 '17 at 16:23
  • 1
    @bricktop Read [this answer](https://stackoverflow.com/a/38676582/3074564) for details about the commands __SETLOCAL__ and __ENDLOCAL__. – Mofi Sep 02 '17 at 16:25
1

here is a method where you can use delayedexpansion without the use of escape characters.

@echo off
setlocal disabledelayedexpansion
set myfile="10 Products You’ll Never Buy Again Knowing How They Are Made!.mp4"
echo %DB_password%
setlocal enabledelayedexpansion
echo !myfile! 
Gerhard
  • 22,678
  • 7
  • 27
  • 43
  • 1
    It would be good to explain what exactly happens on the two lines `setlocal disabledelayedexpansion` and `setlocal enabledelayedexpansion` as there is much more done than just disabling/enabling delayed expansion which could result in a stack overflow or other unexpected behavior depending on rest of batch code. See bottom half of [this answer](https://stackoverflow.com/a/38676582/3074564) on what the commands `setlocal` and `endlocal` always do. – Mofi Aug 31 '17 at 13:42
  • when this didn't work for me it lead me to the problem I was having. I was calling the namer script within enabled expansion. however I am still having trouble with my scripts, I will continue to debug them. – Bricktop Sep 01 '17 at 11:10