I have this script that removes troublesome characters from filenames (along with some esthetic changes):
set "fullpath=%~dp1"
set "file=%~n1"
set "ext=%~x1"
echo "%~nx1" | find /i ".tmp" >nul && exit /b
:: replace problem characters (add additional dash replacements via perl)
set "original=%file%"
set "file=%file:!=%"
set "file=%file:#=%"
set "file=%file:’='%"
set "file=%file:“=%"
set "file=%file:”=%"
set "file=%file:at&t=ATT%"
set "file=%file:&=and%"
:: additional requirements
set "file=%file: _ = - %"
set "file=%file: = %"
set "file=%file: = %"
:: replace underscores and dots only if used as spaces
echo "%file%"| find " " >nul || set "file=%file:_= %"
echo "%file%"| find " " >nul || set "file=%file:.= %"
:: exclamation point has to be removed before we enable delayed expansion
if not "%file%"=="%original%" ren "%fullpath%%original%%ext%" "%file%%ext%" 2>nul && echo validated "%original%%~x1"
:: remove percent sign
setlocal enabledelayedexpansion
set "original=!file!"
:: this percent replacement produces an error (because was not removed)
set "file=!file:%%=_percent!"
if not "%file%"=="%original%" ren "%fullpath%%original%%ext%" "%file%%ext%" 2>nul && echo validated "%original%%ext%"
set "original=!file!"
:: this needs to remain 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 "%fullpath%%original%%ext%" "%file%%ext%" && echo validated "%original%"
endlocal & set "file=%file%" & set "ext=%ext%" & set "fullpath=%fullpath%" & set "full=%fullpath%%file%%ext%"
this is the log for the first file. looks like the original filename doesn't have all the characters originally...
d:\VIDEOS\SHORTS\MEDIA RELATED>setlocal enabledelayedexpansion
d:\VIDEOS\SHORTS\MEDIA RELATED>set "original=!file!"
d:\VIDEOS\SHORTS\MEDIA RELATED>set "file=!file:%=_percent!"
d:\VIDEOS\SHORTS\MEDIA RELATED>if not "SUPERCUT_ Where's The F#ing Money__" == "SUPERCUT_ Where's The F#ing Money__" ren "d:\VIDEOS\SHORTS\MEDIA RELATED\SUPERCUT_ Where's The F#ing Money__.mp4" "SUPERCUT_ Where's The F#ing Money__.mp4" 2>nul && echo validated "SUPERCUT_ Where's The F#ing Money__.mp4"
...because these are the full filenames
SUPERCUT_ Where's The F##%ing Money_!_!.mp4
Through The Fire And Flames 100% Expert Guitar Hero 3.mp4
Top 10 F_#% Ups in Video Game History.mp4
any suggestions, improvements welcomed.
The problem is the way you call your batch.
call has some nasty side effects, as it runs the parser a second time and it has also a special rule for carets to double them.
But you can avoid it by using a variable instead.
for /f "delims=" %%v in ('dir /b /a-d 2^>nul') do (
set "myFile=%cd%\%%v"
call retype
)
And in retype.bat
#echo off
for /f "tokens=*" %%f in ("%myFile%") do (
set "fullpath=%%~dpf"
set "file=%%~nf"
set "ext=%%~xf"
echo "%~nxf" | find /i ".tmp" >nul && exit /b
)
Related
I got a file containing a string on each line like this:
fruit-apple-1.5.6
vegtable-sla-mc5-6.5-16515
extra-huh-9.5-511-515
extra-3.2
I am iterating over it and want it to remove the part of the string on the right after in find "-" + any number "-0","-1","-2","-9",...
so output should be
fruit-apple
vegtable-sla-mc5
extra-huh
extra
this is code i have but it only works with a "-" i cant combine it so it takes "-" + any number like "-1","-5","-2",...
for /f "delims=|" %%A in ("!fileNameCheck:-=|!") do (
echo stripped string = %%A
)
complete code not necessary i think but in case u need it below
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set "RawPath=%~dp0"
FOR /F "USEBACKQ TOKENS=*" %%M IN ("%RawPath%/mods") DO (
REM for %%f in (*.jar) do (
Set "fileNameCheck=%%M"
for /f "delims=|" %%A in ("!fileNameCheck:-=|!") do (
Echo [46m%%A[0m
if exist "%~dp0%%A*.jar" (
REM echo [32mFound %%A "%~dp0%%A*.jar"[0m
if exist "%~dp0%%M" (
REM echo [42mUp to Date[0m [32m%%A "%~dp0%%M"[0m
) else (
for %%j in (*.jar) do (
echo %%j |find "%%A" >nul
if not errorlevel 1 (
echo [41mDifferent Version[0m [31m%%j [0m[90mNewer version[0m [32m%%M[0m
)
)
)
) else (
REM echo [31mMissing %%A[0m
)
)
)
pause
Given that the strings do not contain any of the caracters ?, *, < and >, the following script should do the trick:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~dp0."
set "_FILE=mods.txt"
rem // Read file line by line:
for /F usebackq^ delims^=^ eol^= %%L in ("%_FILE%") do (
rem // Store current line, initialise variables:
set "LINE=%%L" & set "PREV=-" & set "COLL=-"
setlocal EnableDelayedExpansion
rem /* Split line string at every `-` and iterate through items;
rem (`?`, `*`, `<`, `>` must not occur in the string!): */
for %%I in ("!LINE:-=" "!") do (
endlocal & set "ITEM=%%~I"
rem // Check whether item only consists of numerals and `.`:
(
rem // This loop executes when there are other characters:
for /F "delims=0123456789. eol=." %%J in ("%%~I") do (
setlocal EnableDelayedExpansion
rem /* Rebuilt string with currently found items; if there
rem are other items following numeric ones, keep them: */
for /F "delims=" %%K in ("!COLL!!PREV:~1!-!ITEM!") do (
endlocal & set "COLL=%%K" & set "PREV=-"
)
)
) || (
rem // This section runs when there are numerals and `.`:
setlocal EnableDelayedExpansion
for /F "delims=" %%K in ("!PREV!-!ITEM!") do (
rem /* Store numeric items in a separate buffer; if there
rem are no other items following, this is dismissed: */
endlocal & set "PREV=%%K"
)
)
setlocal EnableDelayedExpansion
)
rem // Return rebuilt line string:
echo(!COLL:~2!
endlocal
)
endlocal
exit /B
The example input file mods.txt:
fruit-apple-1.5.6
vegtable-sla-mc5-6.5
extra-huh-9.5
extra-3.2
test-9.15.5
keep-forge
name-subname-10.5-55.5
globalxp-1.16.5
mix-1.2-string-10.5-55.5-more
mix-1.2-string-10.5-55.5-more-3.4
mix-1.2-string-10.5-55.5-more-3.4-5.6-7
8.9
1.2.3-lead
1.2.3-4.5.6-7.8.9-lead-mult
leads to the following output:
fruit-apple
vegtable-sla-mc5
extra-huh
extra
test
keep-forge
name-subname
globalxp
mix-1.2-string-10.5-55.5-more
mix-1.2-string-10.5-55.5-more
mix-1.2-string-10.5-55.5-more
1.2.3-lead
1.2.3-4.5.6-7.8.9-lead-mult
One way is to replace the - with \ and then use variable substitution to strip the %%~ni part from the string. We just need to remove the first and last occurrence then from the string. findstr will determine that the end of string needs to be -num.*
#echo off
setlocal enabledelayedexpansion
for /f "delims=" %%A in ('dir /b /s *.jar ^| findstr /RC:"-[0-9].*$"') do (
set "line=%%~nxA"
for %%i in ("!line:-=\!") do set "final=%%~pi"
set "final=!final:~0,-1!-"
echo !final! | findstr /RC:"\\[0-9].*$" >nul 2>&1
if !errorlevel! equ 0 (
for %%s in ("!final!") do set "final=%%~ps"
set final=!final!
)
set "final=!final:~0,-1!"
set "final=!final:~1!"
echo !final:\=-!
)
the edited code should take care of the additional - as per your comment example name-subname-10.5-55.5
Here's an untested idea, which may not be particularly quick, especially if you have a large number of strings in your mods file:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
For /F %%G In ('Echo Prompt $E ^| %SystemRoot%\System32\cmd.exe /D'
) Do Set "$E=%%G"
For /F Delims^=^ EOL^=^ UseBackQ %%G In ("%~dp0mods") Do (Set "Line=%%G"
SetLocal EnableDelayedExpansion
For /L %%H In (0 1 9) Do For /F Delims^=^ EOL^= %%I In (
'(Echo(^) ^| Set /P "=!Line:-%%H="^&:"!" 0^<NUL') Do Set "Line=%%I"
Echo(%$E%[46m!Line!%$E%[0m
EndLocal
)
Pause
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?
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.
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!
In a batch file I get a folder with a list of subfolders with an unknown number of underscores e.g.:
a_b_10
c_d_e_2
f_17
I need to remove the last token of the names i.e.
a_b
c_d_e
f
Thanks
You could try to get the last part with an underscore and then remove this from your string, but this only works when the last part is unique.
In your sample the last part seems to be always a number in spite of the other parts.
This uses the trick to parse parts of a string by replace the delimiter by a linefeed character.
#echo off
setlocal EnableDelayedExpansion
set LF=^
for /F "delims=" %%X in ('dir /b /AD') do (
call :removeLastPart "%%~X"
)
exit /b
:removeLastPart
set "str=%~1"
for %%L in ("!LF!") DO (
for /F "delims=" %%P in ("!str:_=%%~L!") do set "last=%%P"
)
echo The last part is '!last!'
REM ** now remove the last part by replacing with nothing **
set "str=!str:_%last%=!"
echo !str!
exit /b
#echo off
set "root_dir=C:\scriptests"
setlocal enableDelayedExpansion
for /f %%d in ('dir /b /a:d *_*') do (
call :lastindexof "%%d" _ lio
set "f_name=%%~d"
echo renaming !f_name!
for %%S in (!lio!) do ren !f_name! !f_name:~0,%%S!
)
endlocal
exit /b 0
:lastindexof [%1 - string ; %2 - find last index of ; %3 - if defined will store the result in variable with same name]
#echo off
setlocal disableDelayedExpansion
set "str=%~1"
set "splitter=%~2"
set LF=^
rem ** Two empty lines are required
setlocal enableDelayedExpansion
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!splitter!") do (
set "var=!str:%%R=%%L!"
)
)
for /f delims^=^" %%P in ("!var!") DO (
set "last_part=%%~P"
)
if "!last_part!" equ "" if "%~3" NEQ "" (
echo "not contained" >2
endlocal
set %~3=-1
exit
) else (
echo "not contained" >2
endlocal
echo -1
)
call :strlen0.3 str strlen
call :strlen0.3 last_part plen
call :strlen0.3 splitter slen
set /a lio=strlen-plen-slen
endlocal & set lio=%lio%
endlocal & if "%~3" NEQ "" (set %~3=%lio%) else echo %lio%
exit /b 0
:strlen0.3 StrVar [RtnVar]
setlocal EnableDelayedExpansion
set "s=#!%~1!"
set "len=0"
for %%A in (2187 729 243 81 27 9 3 1) do (
set /A mod=2*%%A
for %%Z in (!mod!) do (
if "!s:~%%Z,1!" neq "" (
set /a "len+=%%Z"
set "s=!s:~%%Z!"
) else (
if "!s:~%%A,1!" neq "" (
set /a "len+=%%A"
set "s=!s:~%%A!"
)
)
)
)
endlocal & if "%~2" neq "" (set %~2=%len%) else echo **%len%**
exit /b
This solution will create the "renfolders.bat.txt" file for you to check in notepad, and run it as a batch file if you are happy with it.
This uses a helper batch file called repl.bat - download from: https://www.dropbox.com/s/qidqwztmetbvklt/repl.bat
Place repl.bat in the same folder as the batch file or in a folder that is on the path.
dir *_* /b /s /ad |repl ".*\\(.*)_.*" "ren \q$&\q \q$1\q" xa >"renfolders.bat.txt"
I just figured out this solution:
for /f %%f in ('dir /b /AD c:\MainFolder\') do (
set var=%%f
set var=!var:_= !
set /a count=0
for %%i in (!var!) do (set /a count+=1)
set /a count2=0
for %%i in (!var!) do (
set /a count2+=1
if /I !count2! equ 1 (set var2=%%i) else if not !count2! equ !count! (set var2=!var2!_%%i)
)
echo !var2!
)
Here is a novel approach
For each folder containing at least one _ in name:
create a temporary empty file with the same name
rename the temporary file, stripping off everything after the final _
read the new name into a variable
strip off the final _ from the name
For an explanation of how the rename works, see How does the Windows RENAME command interpret wildcards?
#echo off
setlocal enableDelayedExpansion
set "loc=%temp%\removeToken"
md "%loc%"
for /d %%F in (*_*) do (
copy nul "%loc%\%%F" >nul
ren "%loc%\*" "*_"
for %%A in ("%loc%\*") do set "new=%%~nxA"
del /q "%loc%\*"
echo old=%%F
echo new=!new:~0,-1!
echo(
)
rd "%loc%"
EDITED - Wrong copy of the code posted
It separates the elements of the directory name and iterates over them discarting the last element
#echo off
setlocal enabledelayedexpansion
for /d %%a in (*_*) do (
set "old=%%~na" & set "new=" & set "previous="
for %%b in ("!old:_=" "!") do (
if not defined previous (
set "previous=%%~b"
) else if not defined new (
set "new=!previous!"
) else set "new=!new!_!previous!"
set "previous=%%~b"
)
echo ren "%%~fa" "!new!"
)
endlocal
I need to get file name with out the file extension, folder name out putted to a csv file. I am able to get file name and folder name using:
#ECHO OFF
SETLOCAL
PUSHD "%~1"
FOR /f "delims=" %%i IN ("%cd%") DO SET directory=%%~nxi
(
FOR /f "delims=" %%i IN ('dir /b /a-d /on') DO (
SETLOCAL enabledelayedexpansion
ECHO "%%i","!directory!"
endlocal
)
)>filelist.csv
How can I rewrite this so the file extension is removed and if there are subfolders it will grab the subfolder name too?
#echo off
setlocal enableextensions disabledelayedexpansion
if not "%~1"=="" (
(for /f "tokens=*" %%i in ('dir /s /b /on "%~1\*"') do (
set "file=%%~dpni"
setlocal enabledelayedexpansion
echo(!file:%~dp1=!
endlocal
)) > filelist.csv
) else (
call "%~f0" "%cd%"
)
endlocal
Not sure about the final format. Try and comment.
EDITED - to handle case exposed by Andriy M
#echo off
setLocal
pushd "%~1"
set "cur_path=%cd:~2%"
setLocal enableDelayedExpansion
FOR /f "delims=" %%i IN ('dir /b /s /a-d /on') DO (
SET "file_path=%%~dpni"
SET "file_path=!file_path:~2!"
SET "file_path=!file_path:%cur_path%=!"
ECHO "!file_path!","%%~di%cur_path%"
)
endLocal
endLocal
EDIT
with additional ~ replacing (comparatively slow - could be optimized with macros... ):
#echo off
pushd .
set "cur_path=%cd:~2%"
call :wavereplacer "%cur_path%" "-" nw_cur_path
setlocal enableDelayedExpansion
FOR /f "delims=" %%i IN ('dir /b /s /a-d /on') DO (
SET "file_path=%%~dpni"
SET "file_path=!file_path:~2!"
SET "file_path=!file_path:%cur_path%=!"
CALL :wavereplacer "!file_path!" "-" file_path
ECHO "!file_path!","%%~di%nw_cur_path%"
)
endlocal
endlocal
goto :eof
:wavereplacer String Replacer [RtnVar]
setlocal
rem the result of the operation will be stored here
set "result=#%~1#"
set "replacer=%~2"
call :strlen0.3 result wl
call :strlen0.3 replacer rl
:start
set "part1="
set "part2="
rem splitting the string on two parts
for /f "tokens=1* delims=~" %%w in ("%result%") do (
set "part1=%%w"
set "part2=%%x"
)
rem calculating the count replace strings we should use
call :strlen0.3 part1 p1l
call :strlen0.3 part2 p2l
set /a iteration_end=wl-p1l-p2l
rem creating a sequence with replaced strings
setlocal enableDelayedExpansion
set "sequence="
for /l %%i in (1,1,%iteration_end%) do (
set sequence=!sequence!%replacer%
)
endlocal & set "sequence=%sequence%"
rem adjust the string length
set /a wl=wl+iteration_end*(rl-1)
rem replacing for the current iteration
set result=%part1%%sequence%%part2%
rem if the second part is empty the task is over
if "%part2%" equ "" (
set result=%result:~1,-1%
goto :endloop
)
goto :start
:endloop
endlocal & if "%~3" neq "" (set %~3=%result%) else echo %result%
exit /b
:strlen0.3 StrVar [RtnVar]
setlocal EnableDelayedExpansion
set "s=#!%~1!"
set "len=0"
for %%A in (2187 729 243 81 27 9 3 1) do (
set /A mod=2*%%A
for %%Z in (!mod!) do (
if "!s:~%%Z,1!" neq "" (
set /a "len+=%%Z"
set "s=!s:~%%Z!"
) else (
if "!s:~%%A,1!" neq "" (
set /a "len+=%%A"
set "s=!s:~%%A!"
)
)
)
)
endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b
In batch, how would I remove all non alphanumeric (a-z,A-Z,0-9,_) characters from a variable?
I'm pretty sure I need to use findstr and a regex.
The solutionof MC ND works, but it's really slow (Needs ~1second for the small test sample).
This is caused by the echo "!_buf!"|findstr ... construct, as for each character the pipe creates two instances of cmd.exe and starts findstr.
But this can be solved also with pure batch.
Each character is tested if it is in the map variable
:test
set "_input=Th""i\s&& is not good _maybe_???"
set "_output="
set "map=abcdefghijklmnopqrstuvwxyz 1234567890"
:loop
if not defined _input goto endLoop
for /F "delims=*~ eol=*" %%C in ("!_input:~0,1!") do (
if "!map:%%C=!" NEQ "!map!" set "_output=!_output!%%C"
)
set "_input=!_input:~1!"
goto loop
:endLoop
echo(!_output!
And it could be speed up when the goto loop is removed.
Then you need to calculate the stringLength first and iterate then with a FOR/L loop over each character.
This solution is ~6 times faster than the above method and ~40 times faster than the solution of MC ND
set "_input=Th""i\s&& is not good _maybe_!~*???"
set "_output="
set "map=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890"
%$strLen% len _input
for /L %%n in (0 1 %len%) DO (
for /F "delims=*~ eol=*" %%C in ("!_input:~%%n,1!") do (
if "!map:%%C=!" NEQ "!map!" set "_output=!_output!%%C"
)
)
exit /b
The macro $strlen can be defined with
set LF=^
::Above 2 blank lines are required - do not remove
#set ^"\n=^^^%LF%%LF%^%LF%%LF%^^":::: StrLen pResult pString
set $strLen=for /L %%n in (1 1 2) do if %%n==2 (%\n%
for /F "tokens=1,2 delims=, " %%1 in ("!argv!") do (%\n%
set "str=A!%%~2!"%\n%
set "len=0"%\n%
for /l %%A in (12,-1,0) do (%\n%
set /a "len|=1<<%%A"%\n%
for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"%\n%
)%\n%
for %%v in (!len!) do endlocal^&if "%%~b" neq "" (set "%%~1=%%v") else echo %%v%\n%
) %\n%
) ELSE setlocal enableDelayedExpansion ^& set argv=,
EDITED - #jeb is right. This works but is really, really slow.
#echo off
setlocal enableextensions enabledelayedexpansion
set "_input=Th""i\s&& is not good _maybe_???"
set "_output="
:loop
if not defined _input goto endLoop
set "_buf=!_input:~0,1!"
set "_input=!_input:~1!"
echo "!_buf!"|findstr /i /r /c:"[a-z 0-9_]" > nul && set "_output=!_output!!_buf!"
goto loop
:endLoop
echo !_output!
endlocal
So, back to the drawing board. How to make it faster? lets try to do as less operations as we can and use as much long substring as we can. So, do it in two steps
1.- Remove all bad characters that can generate problems. To do it we will use the hability of for command to identify these chars as delimiters , and then join the rest of the sections of god characters of string
2.- Remove the rest of the bad characters, locating them in string using the valids charactes as delimiters to find substrings of bad characters, replacing then in string
So, we end with (sintax adapted to what has been answered here)
#echo off
setlocal enableextensions enabledelayedexpansion
rem Test empty string
call :doClean "" output
echo "%output%"
rem Test mixed strings
call :doClean "~~asd123#()%%%^"^!^"~~~^"""":^!!!!=asd^>^<bm_1" output
echo %output%
call :doClean "Thi\s&& is ;;;;not ^^good _maybe_!~*???" output
echo %output%
rem Test clean string
call :doClean "This is already clean" output
echo %output%
rem Test all bad string
call :doClean "*******//////\\\\\\\()()()()" output
echo "%output%"
rem Test long string
set "zz=Thi\s&& is not ^^good _maybe_!~*??? "
set "zz=TEST: %zz%%zz%%zz%%zz%%zz%%zz%%zz%%zz%%zz%%zz%%zz%%zz%%zz%%zz%%zz%%zz%%zz%%zz%%zz%%zz%"
call :doClean "%zz% TEST" output
echo %output%
rem Time long string
echo %time%
for /l %%# in (1 1 100) do call :doClean "%zz%" output
echo %time%
exit /b
rem ---------------------------------------------------------------------------
:doClean input output
setlocal enableextensions enabledelayedexpansion
set "map=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 "
set "input=%~1"
set "output="
rem Step 1 - Remove critical delimiters
(
:purgeCritical
for /L %%z in (1 1 10) do (
for /f tokens^=1^-9^,^*^ delims^=^=^"^"^~^;^,^&^*^%%^:^!^(^)^<^>^^ %%a in ("!input!") do (
set "output=!output!%%a%%b%%c%%d%%e%%f%%g%%h%%i"
set "input=%%j"
)
if not defined input goto outPurgeCritical
)
goto purgeCritical
)
:outPurgeCritical
rem Step 2 - remove any remaining special character
(
:purgeNormal
for /L %%z in (1 1 10) do (
set "pending="
for /f "tokens=1,* delims=%map%" %%a in ("!output!") do (
set "output=!output:%%a=!"
set "pending=%%b"
)
if not defined pending goto outPurgeNormal
)
goto purgeNormal
)
:outPurgeNormal
endlocal & set "%~2=%output%"
goto :EOF
Maybe not the fastest, but at least a "decent" solution
#echo eof
call :purge "~~asd123#()%%%^"^!^"~~~^:^=asd^>^<bm_1" var
echo (%var%)
goto :eof
:purge StrVar [RtnVar]
setlocal disableDelayedExpansion
set "str1=%~1"
setlocal enableDelayedExpansion
for %%a in ( - ! # # $ % ^^ ^& + \ / ^< ^> . ' [ ] { } ` ^| ^" ) do (
set "str1=!str1:%%a=!"
)
rem dealing with some delimiters
set "str1=!str1:(=!"
set "str1=!str1:)=!"
set "str1=!str1:;=!"
set "str1=!str1:,=!"
set "str1=!str1:^^=!"
set "str1=!str1:^~=!"
set "temp_str="
for %%e in (%str1%) do (
set "temp_str=!temp_str!%%e"
)
endlocal & set "str1=%temp_str%"
setlocal disableDelayedExpansion
set "str1=%str1:!=%"
set "str1=%str1::=%"
set "str1=%str1:^^~=%"
for /f "tokens=* delims=~" %%w in ("%str1%") do set "str1=%%w"
endlocal & set "str1=%str1%"
endlocal & if "%~2" neq "" (set %~2=%str1%) else echo %str1%
goto :eof
Still cannot deal with ~ and = but working on it
EDIT: = now will be cleared
EDIT: ~ now will be cleared