Batch file: How to replace "=" (equal signs) and a string variable? - string

Besides SED, how can an equal sign be replaced?
And how can I use a string variable in string replacement?
Consider this example:
For /F "tokens=*" %%B IN (test.txt) DO (
SETLOCAL ENABLEDELAYEDEXPANSION
SET t=is
SET old=%%B
SET new=!old:t=!
ECHO !new!
ENDLOCAL
)
:: SET new=!old:==!
Two problems:
First, I cannot use the variable %t% in !:=!.
SET t=is
SET old=%%B
SET new=!old:t=!
Second, I cannot replace the equal sign in the command line
SET new=!old:==!

I just created a simple solution for this myself, maybe it helps someone.
The disadvantage (or advantage, depends on what you want to do) is that multiple equal signs one after another get handled like one single equal sign. (example: "str==ing" gives the same output as "str=ing")
#echo off
set "x=this is=an test="
echo x=%x%
call :replaceEqualSign in x with _
echo x=%x%
pause&exit
:replaceEqualSign in <variable> with <newString>
setlocal enableDelayedExpansion
set "_s=!%~2!#"
set "_r="
:_replaceEqualSign
for /F "tokens=1* delims==" %%A in ("%_s%") do (
if not defined _r ( set "_r=%%A" ) else ( set "_r=%_r%%~4%%A" )
set "_s=%%B"
)
if defined _s goto _replaceEqualSign
endlocal&set "%~2=%_r:~0,-1%"
exit /B
As you have seen, you use the function like this:
call :replaceEqualSign in variableName with newString

The setlocal enableDelayedExpansion should be moved after your old=%%B assignment in case %%B contains !.
The "t" problem is easy to solve within a loop by using another FOR variable
For /F "tokens=*" %%B IN (test.txt) DO (
SET t=is
SET old=%%B
SETLOCAL ENABLEDELAYEDEXPANSION
for /f %%T in ("!t!") do SET new=!old:%%T=!
ECHO !new!
ENDLOCAL
)
There is no simple native batch solution for replacing =. You can iterate through the string, character by character, but that is slow. Your best bet is probably to switch to VBScript or JScript, or use a non-native utility.
If you really want to do this using pure Windows batch commands, there are a couple of interesting ideas at http://www.dostips.com/forum/viewtopic.php?t=1485

UPDATE: The latest version is here: https://github.com/andry81/contools (https://github.com/andry81/contools/tree/HEAD/Scripts/Tools/std/)
You can use some sequence to temporary replace special characters by placeholders like ?00, ?01, ?02 and ?03. I basically use these set of scripts:
replace_sys_chars.bat:
#echo off
rem Description:
rem Script to replace ?, !, %, and = characters in variables by respective
rem ?00, ?01, ?02 and ?03 placeholders.
setlocal DISABLEDELAYEDEXPANSION
set "__VAR__=%~1"
if "%__VAR__%" == "" exit /b 1
rem ignore empty variables
call set "STR=%%%__VAR__%%%"
if "%STR%" == "" exit /b 0
set ?01=!
call set "STR=%%%__VAR__%:?=?00%%"
set "STR=%STR:!=?01%"
setlocal ENABLEDELAYEDEXPANSION
set STR=!STR:%%=?02!
set "STR_TMP="
set INDEX=1
:EQUAL_CHAR_REPLACE_LOOP
set "STR_TMP2="
for /F "tokens=%INDEX% delims== eol=" %%i in ("/!STR!/") do set STR_TMP2=%%i
if "!STR_TMP2!" == "" goto EQUAL_CHAR_REPLACE_LOOP_END
set "STR_TMP=!STR_TMP!!STR_TMP2!?03"
set /A INDEX+=1
goto EQUAL_CHAR_REPLACE_LOOP
:EQUAL_CHAR_REPLACE_LOOP_END
if not "!STR_TMP!" == "" set STR=!STR_TMP:~1,-4!
(
endlocal
endlocal
set "%__VAR__%=%STR%"
)
exit /b 0
restore_sys_chars.bat:
#echo off
rem Description:
rem Script to restore ?, !, %, and = characters in variables from respective
rem ?00, ?01, ?02 and ?03 placeholders.
setlocal DISABLEDELAYEDEXPANSION
set "__VAR__=%~1"
if "%__VAR__%" == "" exit /b 1
rem ignore empty variables
call set "STR=%%%__VAR__%%%"
if "%STR%" == "" exit /b 0
setlocal ENABLEDELAYEDEXPANSION
set STR=!STR:?02=%%!
set STR=!STR:?03==!
(
endlocal
set "STR=%STR%"
)
set "STR=%STR:?01=!%"
set "STR=%STR:?00=?%"
(
endlocal
set "%__VAR__%=%STR%"
)
exit /b 0
Example:
#echo off
setlocal DISABLEDELAYEDEXPANSION
for /F "usebackq tokens=* delims=" %%i in ("test.txt") do (
set VALUE=%%i
call :PROCESS
)
exit /b 0
:PROCESS
if "%VALUE%" == "" exit /b 0
set "VALUE_=%VALUE%"
call replace_sys_chars.bat VALUE_
rem do variable arithmetic here as usual
if not "%VALUE_:?00=%" == "%VALUE_%" echo."%VALUE%" having ?
if not "%VALUE_:?01=%" == "%VALUE_%" echo."%VALUE%" having !
if not "%VALUE_:?02=%" == "%VALUE_%" echo."%VALUE%" having %%
if not "%VALUE_:?03=%" == "%VALUE_%" echo."%VALUE%" having =
rem restore it
call restore_sys_chars.bat VALUE_
echo "VALUE=%VALUE_%"
echo.---
test.txt:
111/222
AAA=BBB
CCC=%DDD%
EEE=!FFF!
FFF=?00?01?02?03
Result:
"VALUE=111/222"
---
"AAA=BBB" having =
"VALUE=AAA=BBB"
---
"CCC=%DDD%" having %
"CCC=%DDD%" having =
"VALUE=CCC=%DDD%"
---
"EEE=!FFF!" having !
"EEE=!FFF!" having =
"VALUE=EEE=!FFF!"
---
"FFF=?00?01?02?03" having ?
"FFF=?00?01?02?03" having =
"VALUE=FFF=?00?01?02?03"
---
Features:
You can continue use standard batch variable arithmetic between conversions
You can use character placeholders (?00, ?01, ?02, ?03) as plain variable values

Why not use Edlin? I could not find a way to do this with one initial file and no errors from Edlin, but just ignore them with NUL:.
Strangly, the TYPE %0 includes the whole file even if there's an end of file character between the = and !, using TYPE on the batch file after it has run will not work the same way.
#ECHO OFF
GOTO skip
1,1r=!
e
:skip
SET "new==old============="
ECHO %new% > %TEMP%\var.tmp
TYPE %0 > %TEMP%\edlin.tmp
EDLIN %TEMP%\var.tmp < %TEMP%\edlin.tmp > NUL:
SET /P newnew=<%TEMP%\VAR.TMP
ECHO %newnew%
ERASE %TEMP%\VAR.TMP
ERASE %TEMP%\VAR.BAK
ERASE %TEMP%\edlin.tmp

I was looking into this, because I needed to get rid of = in a string like "test=goingon"
I found that calling a next batchfile with test=goingon as parameters, I have parameters 1, "test" and 2, "goingon", in that batchfile.
So:
batchfile 1:
#echo off
call test2.bat test=goingon
batchfile2:
echo arg1: %1
echo arg2: %2
result:
arg1: test
arg2: goingon

I used Bosj's idea to come up with this. It works.
set s=Abra=Cadabra
echo now you see it %s%
call :ReplaceEqual %s%
echo now you don't %s%
exit /b
:ReplaceEqual
set s=%1_%2
exit /b

My answer from another post, but it applies here, too:
There is an alternative that is easier. Instead of passing in a value that contains an equals sign, try something like a colon instead. Then, through the ability to modify that value (the colon), you can convert it back into an equals. Here is an example:
#echo off
set VALUE1=%1
set VALUE2=%VALUE1::==%
echo value1 = %VALUE1%
echo value2 = %VALUE2%
When you run the batch file, call it like this:
C:\>myBatch name:someValue
The output would be:
value1 = name:someValue
value2 = name=someValue
If the name or value contains a space, you will have other issues to address, though. You will need to wrap the entire string in double quotes. But, then you have the issue of needing to get rid of them. This can also be handled, like this:
#echo off
cls
set PARAM=%1
set BASE=%PARAM:"=%
set PAIR=%BASE::==%
rem Either of these two lines will do the same thing - just notice the 'delims'
rem for /f "tokens=1,2 delims=:" %%a in ("%BASE%") do set NAME=%%a & set VALUE=%%b
rem for /f "tokens=1,2 delims==" %%a in ("%PAIR%") do set NAME=%%a & set VALUE=%%b
for /f "tokens=1,2 delims=:" %%a in ("%BASE%") do set NAME=%%a & set VALUE=%%b
echo param = %PARAM%
echo base = %BASE%
echo pair = %PAIR%
echo name = %NAME%
echo value = %VALUE%
When running this batch file like this:
C:\>myBatch "some name:another value"
The output will be:
param = "some name:another value"
base = some name:another value
pair = some name=another value
name = some name
value = another value
Hope that helps others in their quest to win the fight with batch files.
Mike V.

Related

In batch script, split only last string match in a variable

In a batch script, I need to split only the last string match in a variable in a loop until I no longer have the string match.
Input=Level1/Level2/Level3/Level4/LevelN
(where N can be any number)
Output:
Level1/LeveL2/Level3/Level4
Level1/LeveL2/Level3
Level1/Level2
Level1
I have tried the usual "for /f "delims=/"" loops, but they only output each split of the input variable on an individual line. Besides, the value of "N" can vary. So I can't set the number of tokens to a certain value.
Please help.
This site don't works that way. You must post some code and explain the problems you have with it. In this way you can understand the changes made to your own code. If you request us for code, any code, then you could receive one ("any code") like this:
#echo off
setlocal EnableDelayedExpansion
set "Input=Level1/Level2/Level3/Level4/LevelN"
:loop
set "Output="
set "part=%Input:/=" & set "Output=!Output!/!part!" & set "part=%"
set "Input=%Output:~1%"
if "%Input%" equ "~1" goto exitLoop
echo "%Input%"
goto loop
:exitLoop
Here is a nice recursive approach that makes use of the ~ modifiers, assuming that the input string is provided as a quoted ("") command line argument, which does not begin with /, does not contain two consecutive // and none of the characters ", \, *, ?, <, >:
#echo off
rem // Store argument in variable:
set "INPUT=%~1"
if not defined INPUT exit /B
rem /* Precede with `\` and replace each `/` by `\`, so the resulting string appears to
rem be an absolute path, which can be split by `~` modifiers of `for` variables;
rem the inner `for` loop resolves the split path and removes any `\.` suffix: */
for %%I in ("\%INPUT:/=\%") do for %%J in ("%%~pI.") do set "REST=%%~pnxJ"
rem // Revert replacement of every `/` by `\` and remove the previously preceded `\`:
set "REST=%REST:\=/%"
set "REST=%REST:*/=%"
rem // If there is a string left, output it and call this script recursively:
if defined REST (
setlocal EnableDelayedExpansion
echo(!REST!
endlocal
call "%~f0" "%REST%"
)
A more classical approach, just for fun...
#echo off
setlocal EnableDelayedExpansion
set "Input=Level1/Level2/Level3/Level4/LevelN"
rem Split the string, join it again and store partial results
set "aux="
set "n=0"
for %%a in ("%Input:/=" "%") do (
set "aux=!aux!/%%~a"
set /A n+=1
set "out[!n!]=!aux:~1!"
)
rem Show results in reverse order
set /A n-=1
for /L %%i in (%n%,-1,1) do echo !out[%%i]!
This is easily done with a regex in PowerShell. Since the regex is greedy be default, it will get everything up to the last SOLIDUS and store it in the $1 group.
SET "S=Level1/Level2/Level3/Level4/LevelN"
FOR /F "delims=" %%a IN ('powershell -NoL -NoP "'%S%' -replace '(.*/).*', '$1'"') DO (
SET "RESULT=%%a"
)
ECHO RESULT is set to %RESULT%
Revision:
This outputs all groups and not just the last one.
SET "S=Level1/Level2/Level3/Level4/LevelN"
FOR /F "delims=" %%a IN ('powershell -NoL -NoP -Command ^
"$a = '%S%'.Split('/'); " ^
"for ($i = $a.Count - 2; $i -ge 0; $i--) { $a[0..$i] -join '/' }"') DO (
ECHO %%a
)

How can I reverse a string in Batch?

I found this solution for reversing strings that worked before, but not any more for some reason:
setlocal enabledelayedexpansion
set num=0
:LOOP
call set tmpa=%%advanced:~%num%,1%%%
set /a num+=1
if not "%tmpa%" equ "" (
set string1=%tmpa%%string1%
goto LOOP
)
My message that I receive is:
The input line is too long.
The syntax of the command is incorrect.
I simply need to reverse the string in the variable %advanced% and output it to %string1%.
If reversing strings can be done in one line, that would be super helpful in the project I am working on. If it can be done without the setlocal enabledelayedexpansion, that would be even more helpful but I doubt it is be possible.
Another way using a BAT/VBS :
#echo off
set "advanced=1234567890"
echo WScript.Echo StrReverse("%advanced%"^) >reverse.vbs
for /f "delims=" %%a in ('cscript //nologo reverse.vbs') do set "$reversed=%%a"
>nul del reverse.vbs
echo reversed string --^> %$reversed%
You said it first worked and later not anymore; I think simply clearing variable string1 will do the trick. Your code should work as is.
Next, you do not need to enable delayed expansion as you are not using it (!! expansion) anyway.
Finally, instead of asking whether tmpa is empty, you could query whether it is defined (but this is a matter of taste though).
set string1=
set num=0
:LOOP
call set tmpa=%%advanced:~%num%,1%%%
set /a num+=1
if defined tmpa (
set string1=%tmpa%%string1%
goto :LOOP
)
In this way it works if you change to set string1=!tmpa!!string1!
#echo off
setlocal enabledelayedexpansion
set "advanced=abcdefghijklmnopqrstuvwxyz!"
set "num=0"
:LOOP
call set "tmpa=%%advanced:~%num%,1%%%"
set /a num+=1
if not "%tmpa%" equ "" (
set "string1=!tmpa!!string1!"
goto LOOP
)
echo !string1!
endlocal
Another way a bit faster:
#echo off
setlocal enabledelayedexpansion
set "advanced=abcdefghijklmnopqrstuvwxyz"
echo %advanced%>"%tmp%\alpha.tmp"
for %%l in ("%tmp%\alpha.tmp") do set /a len=%%~zl
set /a len-=2
set "reverse="
set "char="
for /l %%i in (0,1,%len%) do (
for /f "usebackq" %%a in ("%tmp%\alpha.tmp") do (
set "char=%%a"
set "reverse=!char:~%%i,1!!reverse!"
)
)
del "%tmp%\alpha.tmp"
echo !reverse!
endlocal
To reverse a string or file contents:
REM eg. set string/content to file
set /p="Hello, world!"> test
certutil -encodehex -f test temp 4
REM reverse file contents
set rev=
(for /f "delims=" %i in (temp) do for %j in (%i) do set rev=%j !rev!)
set /p="%rev:~0,-6%">temp
certutil -decodehex temp out.txt
REM view content
more out.txt
Tested in Win 10 CMD

How to generate string for bruteforce attack using batchfile

I have some code which can crack numeric rar file passwords. The code just increments the value of a variable (starting from 0) and I use that to check against the password to unrar using unrar command.
But I want to generate strings for brute force attacks.
SET PASSWORD=0
:START
SET /A PASSWORD=%PASSWORD%+1
UNRAR E -INUL -P%PASSWORD% "%PATH%\%NAME%" "%DESTINATION%"
IF /I %ERRORLEVEL% EQU 0 GOTO CLOSE
GOTO START
:CLOSE
echo Password Cracked...
echo Password is %PASSWORD%
Here
%PATH% is path where rar file is located
%NAME% is name of rar file
%DESTINATION% is place where file is stored after UNRAR,
In my code DESTINATION is "%TEMP%\%RANDOM%"
By applying this I am able to get the password, but it is not useful for strings which contain alpha characters.
How do I generate strings starting from "a", so I am able to crack alphabetic passwords too?
I consider this a crazy idea to do in CMD/batch, but it at least sounded like an interesting challenge.
So, playing the part of the Professor from Gilligan's Island, I've decided to attempt to build a particle accelerator from coconuts.
Here's my entry. There might likely be a better solution using CMD/batch. The most favorable thing I can say about it is that it works. To adapt it to your purpose, change the ECHO statement inside the :INFINITE_LOOP to do something meaningful, like attempt to decompress the file and exit on success.
Here's a sample of the output as it runs:
'0'
'1'
...
'9'
...
'A'
'B'
...
'Y'
'Z'
'a'
'b'
...
'y'
'z'
'00'
'01'
...
'zy'
'zz'
'000'
'001'
...
'Car'
'Cas'
'Cat'
'Cau'
'Cav'
'Caw'
...
This solution should work with many characters (CHARSET contains all the characters to be used in the output string) with the exception of characters that cannot be assigned simply without escaping them in some manner (e.g. double quote, percent (maybe?), exclamation, ...).
The script doesn't completely clean up after itself (you'll have to manually erase %ITER_FILE%), but it isn't too messy.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "CHARSET=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghikjlmnopqrstuvwxyz"
:: ======================================================================
:: Setup
CALL :CONFIGURE_CHARSET "%CHARSET%"
REM ECHO MAX_INDEX: !MAX_INDEX!
REM SET
:: Put the smallest value in the file.
SET ITER_FILE=%TEMP%\ITERATOR_%RANDOM%.txt
ECHO.0>"%ITER_FILE%"
:: ======================================================================
:: Main Loop
:INFINITE_LOOP
CALL :READ_ITER "%ITER_FILE%" _ITER_CONTENTS
ECHO '!_ITER_CONTENTS!'
CALL :NEXT_ITER "%ITER_FILE%"
GOTO :INFINITE_LOOP
EXIT /B
:: "Increment" the contents of the "state variable" file.
:NEXT_ITER
SETLOCAL
SET "FILE=%~1"
SET "NEXT_FILE=%TEMP%\ITERATOR_NEXT_%RANDOM%.txt"
SET CARRY=1
FOR /F %%n IN (%FILE%) DO (
IF !CARRY! EQU 1 (
SET /A I_VALUE=%%n+1
IF !I_VALUE! GTR %MAX_INDEX% (
SET I_VALUE=0
SET CARRY=1
) ELSE (
SET CARRY=0
)
) ELSE (
SET I_VALUE=%%n
)
ECHO !I_VALUE!>>"!NEXT_FILE!"
)
REM Add a new digit place.
IF !CARRY! EQU 1 (ECHO.0>>"!NEXT_FILE!")
MOVE /Y "%NEXT_FILE%" "%FILE%" >NUL
ENDLOCAL
EXIT /B
:: Read the contents of the "state variable" file and translate it
:: into a string.
:: The file is a series of lines (LSB first), each containing a single
:: number (an index).
:: Each index represents a single character from the CHARSET.
:READ_ITER
SETLOCAL
SET "FILE=%~1"
SET "VAR=%~2"
SET VALUE=
SET _V=
FOR /F %%n IN (%FILE%) DO (
SET "VALUE=!VALUE_%%n!!VALUE!"
)
ENDLOCAL && SET %VAR%=%VALUE%
EXIT /B
:: Translate the index number to a character.
:TRANS_INDEX
SETLOCAL
SET "VAR=%~1"
SET "C=%~2"
SET IDX=
FOR /L %%i IN (0,1,%MAX_INDEX%) DO (
IF "!VALUE_%%i!"=="!C!" SET IDX=%%i
)
SET "TRANS=!VALUE_%%i!"
ENDLOCAL && SET "%VAR%=%TRANS%"
EXIT /B
:: This is ugly magic.
:: Create variables to hold the translation of an index to a character.
:: As a side effect, set MAX_INDEX to the largest used index.
:CONFIGURE_CHARSET
SET CONFIG_TEMP=%TEMP%\CONFIG_%RANDOM%.cmd
IF EXIST "%CONFIG_TEMP%" DEL /Q "%CONFIG_TEMP%"
CALL :WRITE_CONFIG "%CONFIG_TEMP%" "%~1"
REM Import all the definitions.
CALL "%CONFIG_TEMP%"
EXIT /B
REM Create a means to "add one" to a value.
:WRITE_CONFIG
SETLOCAL
SET "FILE=%~1"
SET "STR=%~2"
REM This is the "index" of the symbol.
SET "INDEX=%~3"
IF "!INDEX!"=="" SET INDEX=0
IF NOT "%STR%"=="" (
SET "C=!STR:~0,1!"
IF NOT "%~4"=="" (
SET "FIRST=%~4"
) ELSE (
SET "FIRST=!C!"
)
SET "D=!STR:~1,1!"
IF "!D!"=="" (
SET CARRY=1
SET "D=!FIRST!"
) ELSE (
SET CARRY=0
)
ECHO SET VALUE_!INDEX!=!C!>>"!FILE!"
SET /A NEXT_INDEX=INDEX+1
REM Recurse...
SET MAX_INDEX=!INDEX!
CALL :WRITE_CONFIG "!FILE!" "!STR:~1!" "!NEXT_INDEX!" "!FIRST!"
IF !INDEX! GTR !MAX_INDEX! SET MAX_INDEX=!INDEX!
)
ENDLOCAL && SET MAX_INDEX=%MAX_INDEX%
EXIT /B
Recursion does the job!
I know I'm a bit late, but I think this code works very well and also quite fast:
#echo off
title bruteforce
setlocal enableDelayedExpansion
echo:
echo Brute Force Attack:
echo -------------------
echo:
set /p input="Amount of digits: "
set /a depth=%input%-1
echo:
set /p possibleChars="Possible Characters: "
echo:
for /l %%y in (0, 1, %depth%) do (
set chars[%%y]=0
)
call :next 0
echo:
pause
exit
:next
setLocal
set /a d=%1
for %%x in (%possibleChars%) do (
set chars[%d%]=%%x
if %d% lss %depth% (
call :next !d!+1
) else (
set password=
for /l %%c in (0, 1, %depth%) do (
set password=!!password!!chars[%%c]!!
)
echo !password!
)
)
On my laptop it prints about 1500 combinations per second, and you can do what ever you want with the password-variable which I've just printed out!
IMPORTANT:
The first part of the program asks you for the length of the password to crack and possible characters when you run it!
You have to enter the possible characters with a space between each character, like this:
0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k...

Batch: Concatenate two arbitrary strings outside SETLOCAL EnableDelayedExpansion

I need to concatenate two string variables and place the result back in the first variable. These two strings can contain any arbitrary characters, like newline, exclamation, etc.
The main script runs with delayed expansion disabled, so I have to use SETLOCAL EnableDelayedExpansion for actual concatenation. I just don't know how to get the result back out of the local and into the global variable.
I would like to avoid using temporary files.
I wish batch files allowed delayed expansion outside of the local block.
Thanks.
EDIT :
#Jeb
I tried using your code inline, instead of in a function, and it worked.
Then I tried putting it in a FOR loop, and that broke it.
Function call from a loop = works.
Inline is a loop = didn't work for me.
I don't need that functionality right now. This is just an observation.
Thanks.
#echo off
REM Changed from function call to inline implementation
setlocal EnableDelayedExpansion
cls
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
set LF=^
rem TWO Empty lines are neccessary
set "original=zero*? %%~A%%~B%%~C%%~L!LF!one&line!LF!two with exclam^! !LF!three with "quotes^&"&"!LF!four with ^^^^ ^| ^< ^> ( ) ^& ^^^! ^"!LF!xxxxxwith CR!CR!five !LF!six with ^"^"Q ^"^"L still six "
setlocal DisableDelayedExpansion
SET result=""
REM call :lfTest result original
::::::::::::::::::::
for /L %%i in (1,1,2) do (
setlocal
set "NotDelayedFlag=!"
echo(
if defined NotDelayedFlag (echo lfTest was called with Delayed Expansion DISABLED) else echo lfTest was called with Delayed Expansion ENABLED
setlocal EnableDelayedExpansion
set "var=!original!"
rem echo the input is:
rem echo !var!
echo(
rem ** Prepare for return
set "var=!var:%%=%%~2!"
set "var=!var:"=%%~3!"
for %%a in ("!LF!") do set "var=!var:%%~a=%%~L!"
for %%a in ("!CR!") do set "var=!var:%%~a=%%~4!"
rem ** It is neccessary to use two IF's, else the %var% expansion doesn't work as expected
if not defined NotDelayedFlag set "var=!var:^=^^^^!"
if not defined NotDelayedFlag set "var=%var:!=^^^!%" !
set "replace=%% """ !CR!!CR!"
for %%L in ("!LF!") do (
for /F "tokens=1,2,3" %%2 in ("!replace!") DO (
ENDLOCAL
ENDLOCAL
set "result=%var%" !
#echo off
)
)
)
::::::::::::::::::::
setlocal EnableDelayedExpansion
echo The result with disabled delayed expansion is:
if !original! == !result! (echo OK) ELSE echo !result!
echo ------------------
echo !original!
pause
goto :eof
Like I said in your other question: Batch: Returning a value from a SETLOCAL EnableDelayedExpansion
Just follow the link for a "perfect" solution
Or I can paste the code here
rem ** Preparing CR and LF for later use
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
set LF=^
rem TWO Empty lines are neccessary
Then at the start of your function, to detect if delayedExpansion is OFF or ON
setlocal
set "NotDelayedFlag=!"
setlocal EnableDelayedExpansion
And at the end of your function, to return the value
rem ** Prepare for return
set "var=!var:%%=%%~2!"
set "var=!var:"=%%~3!"
for %%a in ("!LF!") do set "var=!var:%%~a=%%~L!"
for %%a in ("!CR!") do set "var=!var:%%~a=%%~4!"
rem ** It is neccessary to use two IF's, else the %var% expansion doesn't work as expected
if not defined NotDelayedFlag set "var=!var:^=^^^^!"
if not defined NotDelayedFlag set "var=%var:!=^^^!%" !
set "replace=%% """ !CR!!CR!"
for %%L in ("!LF!") do (
for /F "tokens=1,2,3" %%2 in ("!replace!") DO (
ENDLOCAL
ENDLOCAL
set "%~1=%var%" !
#echo off
goto :eof
)
)
EDIT: A little bit shorter variation
Ok, this looks a little bit complicated, and in many cases it can be solved with an other trick.
If you know, that you will switch back from an EnableDelayed to DisableDelayed and you are sure not using any LF a FOR-RETURN will work too.
#echo off
setlocal
call :myTest result
set result
goto :eof
:myTest
setlocal EnableDelayedExpansion
rem ... do something here
set "value=^!_&_%%_|_>"
echo --
for /f ^"eol^=^
^ delims^=^" %%a in ("!value!") do (
endlocal
set "%~1=%%a"
goto :eof
)
The splitting of the for /f ^"eol^=^.... is only for disabling the eol character.

batch file renaming "This.is.a.FIlename.mp3" to "tiaf.mp3"

okay so I have a dir with some files. I want to do a specific file-renamingscript
i'm stuck with this part, taking only the first letter of each part of the filename:
if the filename would be
This.is.a.FIle.mp3
I would like to rename it to
tiaf.mp3
notice i want it to be all in lowercase.
The word length is variable so i cant take reference from it as a local variable !variable:~0,2!
anyone could help?
thanx!
edit: i forggot to ask. If you have an idea to make a test if the filename is of the format i mentioned. Because if the file is called. 'file.mp3' then i wouldn't want it to be renamed to 'f.mp3'
This should work, but if you want to allow also "!" exclamation marks in your filenames, it have to be a little bit extended.
#echo off
setlocal EnableDelayedExpansion
for %%f in ("C:\temp\folder\*.*") do (
call :createName "%%~f"
)
goto :eof
:: Compress a filename with more than one dot to only the first (lower) letters of each part
:: one.TWO.three.four.exe to ottf.exe
:createName <dot-filename>
setlocal
set "filename=#.%~n1"
set "ext=%~x1"
set "count=0"
set "short="
:createName.loop
for %%a in ("!filename!") do (
set "part=%%~xa"
set "filename=%%~na"
if defined part (
set /a count+=1
set "char=!part:~1,1!"
call :toLower char
set "short=!char!!short!"
) ELSE (
set "char="
)
rem echo "%%~na"-"%%~xa" "!char!" "!short!"
)
if defined part goto :createName.loop
set "short=!short!!ext!"
if !count! GTR 1 (
echo ren "%~f1" "!short!"
)
(
endlocal
goto :eof
)
:: convert a char to the lower variant or leave it unchanged if it isn't a char
:: use the %var:*n=% syntax to remove the front of a string, to get the correct char
:toLower <variable to char>
setlocal EnableDelayedExpansion
(
set "char=!%~1!"
set "helper=##aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz"
set "lower=!helper:*%char%=!"
set "lower=!lower:~0,1!"
if "!lower!"=="#" set "lower=!char!"
)
(
endlocal
set "%~1=%lower%"
goto :eof
)
Would this kind of logic work for you:
#echo off
for /f "delims=|" %%f in ('dir /b C:\temp') do call :runsub %%f
goto EOF
:runsub
for /f "tokens=1,2,3,4 delims=." %%a in ("%~n1") do set a=%%a&set b=%%b&set c=%%c&set d=%%d
if not "%a%"=="" echo %a%
if not "%b%"=="" echo %b%
if not "%c%"=="" echo %c%
if not "%d%"=="" echo %d%
:EOF
You can change the echo %a%, echo %b%, etc. to sets and get the substring from these. This also only gets the first 4 splits, you can add more if you need. Also change C:\temp to the appropriate directory.

Resources