Batch: Concatenate two arbitrary strings outside SETLOCAL EnableDelayedExpansion - string

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.

Related

Batch Trim Lines in Text File after Sub-String - REM Trim with Expansion

I am running Windows 10 Pro using batch files (open to using VBS and PS1 files) and I have a text file automatically exported by software that can look like this:
Sub_Group691_FE7IP11_2017-12-12.sldasm_bin/parts/Loft-Project.sldasm_bin/parts/Loft-Project...
Sub_Group691_FE7IP12_2017-12-12.sldasm_bin/parts/Loft-Project.sldasm_bin/parts/Loft-Project...
Sub_Group691_FE7IP13_2017-12-12.sldasm_bin/parts/Loft-Project.sldasm_bin/parts/Loft-Project...
Each line continues specifying sub-parts after the "..." and could contain "poison" characters. The Sub_Group part is pulled from the filename and can also contain "poison" characters.
What I am looking to do is export just the filename which is right at the beginning of each line, up to and including the first file extension, in this case ".sldasm." Everything to the right of the first instance of .sldasm should be trimmed.
What I have cobbled together so far from research on Stackoverflow is:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "inputfile=C:\Scratch\ASMExport.txt"
SET "outputfile=C:\Scratch\InputFiles.txt"
(
FOR /f "usebackqdelims=" %%a IN ("%inputfile%") DO (
SET "currentline=%%a"
ECHO("!currentline:.sldasm=.sldasm & rem "!"
)
)>"%outputfile%"
GOTO :EOF
My problem lies with the "rem" line, which does not seem to work as intended either because of being within a FOR loop or because of needing to enable delayed expansion. It seems to be parsing the "& rem" as text, which looks to be because of the way delayed expansion works. What I get from the above lines is:
SubGroup691_FE7IP11_2017-12-12.sldasm" & rem "/bin/parts/Loft-Project.sldasm" & rem ""
SubGroup691_FE7IP12_2017-12-12.sldasm" & rem "/bin/parts/Loft-Project.sldasm" & rem ""
SubGroup691_FE7IP13_2017-12-12.sldasm" & rem "/bin/parts/Loft-Project.sldasm" & rem ""
I can use this same line outside the loop and without ENABLEDELAYEDEXPANSION like this:
#ECHO OFF
SETLOCAL
SET "inputstring=Sub_Group691_FE7IP11_2017-12-12.sldasm_bin/parts/Loft-Project.sldasm_bin/parts/Loft-Project"
SET "outputstring=%inputstring:.sldasm=.sldasm & rem "%"
echo %outputstring%
The output to that would give me what I am looking for:
Sub_Group691_FE7IP11_2017-12-12.sldasm
In searching, I am beginning to think that rem cannot be used in this way, and I must move to a token delimiter loop using a bogus delimiter.
I would be content in getting this to work and not worrying about "poison" characters in the filename by being diligent about naming files correctly.
Thank you for your help!
The & rem approach to truncate strings to the right cannot work with delayed expansion (!), it relies on normal/immediate expansion (%). This is because immediate expansion is done before commands (like rem) are recognised, but delayed expansion happens afterwards, so the rem command is never executed.
However, you could in the loop replace every .sldasm by a forbidden character like | and then split the string by a for /F loop, like this:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "InputFile=C:\Scratch\ASMExport.txt"
set "OutputFile=C:\Scratch\InputFiles.txt"
set "Extension=.sdlasm"
> "%OutputFile%" (
for /F usebackq^ delims^=^ eol^= %%A in ("%InputFile%") do (
set "CurrentLine=%%A"
setlocal EnableDelayedExpansion
set "CurrentLine=!CurrentLine:%Extension%=|!"
for /F "delims=|" %%B in ("!CurrentLine!") do (
endlocal
echo(%%B
setlocal EnableDelayedExpansion
)
endlocal
)
)
endlocal
exit /B
Delayed expansion is toggled so that no for variables become expanded when it is enabled, in order to avoid loss of or problems with exclamation marks.
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q47788829.txt"
FOR /f "usebackqtokens=1delims=/" %%a IN ("%filename1%") DO (
FOR /f "tokens=1*delims=." %%b IN ("%%a") DO (
FOR /f "tokens=1*delims=_" %%d IN ("%%c") DO (
ECHO %%%%-a=%%~a %%%%-b=%%~b %%%%-c=%%~c %%%%-d=%%~d %%%%-e=%%~e ^<^<
)
)
)
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
I used a file named q47788829.txt containing your data for my testing.
You should be able to assemble your required report data from the elements %%a..%%e displayed. All a matter of using tokens and delims constructively.
If your input is the text that you wrote, with the same structure, I think this batch should work and extract the names that you want quickly.
CODE:
#echo off
setlocal enabledelayedexpansion
set "inputFile=input.txt"
set "outputFile=result.txt"
for /f "delims=/ tokens=1" %%A in (%inputFile%) do (
set "tempFileName=%%A"
echo !tempFileName:~0,-4!>> %outputFile%
)
You only had to adjust the variables 'inputFile' and 'outputFile' to your needs.

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

Concatenate string and number in batch file

How to concatenate number and string in for loop?
I've tried like this but it doesn't work:
SET PATH=C:\file
FOR /L %%x IN (1,1,5) DO (
SET "NUM=0%%x"
SET VAR=%PATH%%NUM%
ECHO %VAR%
)
Modify your code like this:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET PATH=C:\file
FOR /L %%x IN (1,1,5) DO (
SET "NUM=0%%x"
SET VAR=%PATH%!NUM!
ECHO !VAR!
)
You always have to use SETLOCAL ENABLEDELAYEDEXPANSION and !...! instead of %...% when working with variables which are modified inside a loop.
You'll need delayed expansion to accomplish that:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET FILEPATH=C:\file
FOR /L %%x IN (1,1,5) DO (
SET "VAR=%FILEPATH%0%%x"
ECHO !VAR!
)
ENDLOCAL & SET VAR=%VAR%
You do not need the interim variable NUM, you can concatenate the string portions immediately.
You should never change variable PATH as this is used by the system. Hence I changed it to FILEPATH.
Since SETLOCAL creates a new localised environment block, all variables defined or changed in there cannot be seen outside of that block. However, the last line in the above code demonstrates how the last concatenated value can be passed beyond ENDLOCAL.

A Batch file to read a file and replace a string with a new one

I want to create a batch file to read every line of a file in a loop and replace a string with another one. Following is my code snippet:
for /F "tokens=*" %%i in (myfile) do (
set str=%%i
set str=%str: %oldstring% = %newstring%%
echo %str% >> newfile
)
This results in a newfile with 'Echo is off' as many lines as there are in myfile. Seems like str variable holds no value at all when assigned to %%i. Can someone help me?
Try out this small script:
#echo off
set val=50
echo %val%
for /l %%i in (1,1,1) do (
set val=%%i
echo %val%
)
echo %val%
pause>nul
The output is:
50
50
1
Not what you expected, right?
That's because in a for loop, variables aren't updated until the loop has finished. To combat this, you can use setlocal enabledelayedexpansion, and replace the percent signs (%) with an exclamation mark (!):
#echo off
setlocal enabledelayedexpansion
set val=50
echo %val%
for /l %%i in (1,1,1,) do (
set val=%%i
echo !val!
)
echo %val%
pause>nul
The output:
50
1
1
The reason the str variable holds no value (during the for loop) is because it hasn't been set beforehand.
So, with these quick modifications, your script will work...
setlocal enabledelayedexpansion
for /f "tokens=*" %%i in (myfile) do (
set str=%%i
set str=!str: %oldstring% = %newstring%!
echo !str! >> newfile
)
By the way, this snippet is assuming that oldstring and newstring won't be set within the forloop, otherwise things will get messy.
Have fun.
having spent some time at this I got the correct way:
#echo off
setlocal enabledelayedexpansion
set oldstring=AF-07295
set /a count=1000
for %%f in (*.*) do (
set /a count=!count!+1
for /f "tokens=*" %%i in (%%f) do (
set str=%%i
call set str=%%str:!oldstring!=!count!%%
echo !str! >> %%~nf.ordnew
)
)
endlocal
setlocal ENABLEDELAYEDEXPANSION
set filein="c:\program files\test1.txt"
set fileout="c:\program files\test2.txt"
set old=#VERSION#
set new=2.0.3
for /f "tokens=* delims=ΒΆ" %%i in ( '"type %filein%"') do (
set str=%%i
set str=!str:%old%=%new%!
echo !str! >> %fileout%
)
working perfect
and isn't removing white spaces at the begining of the lines file

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