String processing using Batch Script - string

I'm currently creating a batch script that has to loop through the lines in a file, checking for some string, and if theres a match prefix that string with a '#' (comment it out).
I'm perfectly new to batch script, all I got this far is:
for /f %%j in (CMakeLists.txt) do (
if "%%j"=="Extensions_AntTweakBar" (
echo lol1
)
if "%%j"=="Extensions_Inspection" (
echo lol2
)
if "%%j"=="Extensions_InspectionBar" (
echo lol3
)
)
So my current issue is, I don't know how to operate on string within batch scripts. If someone could help me out that would be appreciated :)

You can just use the text you want to append followed by your variable generally.
C:\>set MY_VAR=Hello world!
C:\>echo #%MY_VAR%
#Hello world!
C:\>set MY_VAR=#%MY_VAR%
C:\>echo %MY_VAR%
#Hello world!
If you're just doing echo, that's fine. echo #%%j will do what you need.
But if you want to set the line to a variable, you have to enable delayed expansion. Add setlocal ENABLEDELAYEDEXPANSION to the top of your file and then surround your variables with ! instead of %. For example (and notice that I've added delims= to put the entire line in %%j instead of the first word on the line):
#echo off
setlocal ENABLEDELAYEDEXPANSION
set LINE=
for /f "delims=" %%j in (CMakeLists.txt) do (
set LINE=%%j
if "%%j"=="Extensions AntTweakBar" (
set LINE=#%%j
)
if "%%j"=="Extensions Inspection" (
set LINE=#%%j
)
if "%%j"=="Extensions InspectionBar" (
set LINE=#%%j
)
echo !LINE!
)
Given this input file:
Extensions AntTweakBar
some text
Extensions Inspection
Extensions What?
some more text
Extensions InspectionBar
Extensions InspectionBar this line doesn't match because delims= takes all text
even more text
The above script produces this output:
C:\>comment.bat
#Extensions AntTweakBar
some text
#Extensions Inspection
Extensions What?
some more text
#Extensions InspectionBar
Extensions InspectionBar this line doesn't match because delims= takes all text
even more text
And of course removing #echo off will help you debug problems.
But all that being said, you're about at the limit of what you can accomplish with batch string processing. If you still want to use batch commands, you may need to start writing lines to temporary files and using findstr with a regex.

Without a better understanding of what you want inside your loop or what your CMakeLists.txt file looks like, try this on for starters:
FINDSTR "SOMETHING" %%J && ECHO #%%J || ECHO %%J
The && makes the second command (the ECHO) conditional on the first command exiting without an error state, and the || is like a logical OR and it runs when the first one doesn't.
Really, for modifying the internals of a text file you are probably going to be much better off using either sed or awk - win32 binaries can be found in the UnxUtils project.

Related

String Substitution Using Variables in a FOR loop Batch Syntax

I am trying to use string substitution to truncate a list of full file paths down to relative paths in an existing text file. In addition there is some basic automated renaming. The renaming works as intended but the string substitution I cannot get to work. All the documentation I could find describing string substitution used standalone strings, not strings stored in variables. So I do not know and cannot figure out the proper syntax. I have also read similar questions asked here and attempted those solutions to no avail.
Everything in my script works as intended except for this line:
SET %%I=%%%I:%Temp_Dir%=%
It does not change %%I at all. The input to the FOR loop %List% is a text file that looks like this:
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin\codesegment.o
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin\graphic.o
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin\helper.o
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin\main.o
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin\game.out
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out
The final output I get right now is identical to the above list.
The desired output should look like this:
\out\bin\codesegment.o
\out\bin\graphic.o
\out\bin\helper.o
\out\bin\main.o
\out\bin\game.out
\out\bin
\out
I know the syntax is supposed to be:
SET string = C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin\codesegment.o
SET string = %string:C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working =%
As I said though, I cannot get it to work using variables in a FOR loop. I am also attempting this method of string substitution because the path of %Temp_Dir% is always at the start of every line and is always found in each line once.
Here is the whole script for reference. I would prefer a one line solution like the one I was attempting. I am aware longer solutions are available but due to reasons beyond my control the one-line string substitution is required.
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET Proj_Dir="C:\Users\UserName\Desktop\Project"
SET Temp_Dir=%temp%\Batch_Renaming_Temp\Working
SET Counter=1
SET List="%temp%\Batch_Renaming_Temp\LFN_LIST.TMP"
SET List_Final="%temp%\Batch_Renaming_Temp\LFN_LIST.TXT"
ROBOCOPY /E /MIR %Proj_Dir% "%Temp_Dir%" > NUL
CD "%Temp_Dir%"
DIR /A-D /O:N /B /S > %List%
DIR /AD /O:N /B /S | SORT /R >> %List%
TYPE NUL > %List_Final%
FOR /F "usebackq tokens=* delims=" %%I IN (%List%) DO (
REN "%%I" !Counter!
SET /A !Counter+=1
SET %%I=%%%I:%Temp_Dir%=%
ECHO %%I >> %List_Final%
)
Like #Squashman pointed out in the comments, you cannot "set" a FOR variable.
If your variable depends on other variables indirectly, you need to use CALL SET or delayed expansion.
The easiest solution so far:
(for /F "usebackq tokens=8* delims=\" %%x in (%List%) do echo \%%y) > %List_Final%
It uses \ as a delimiter and pass the 8+th arguments to %%y, and redirects all output to %List_Final%
Tested on a Windows 10 laptop, works perfectly.

Catenating Variables in bat file

I've created bat file named vk.bat . The code is as following :-
SET "tcs="
FOR %%A IN (%*) DO (
Set "tcs=%tcs% -t %%A"
)
Echo %tcs%
I am executing this bat from cmd as following :-
c:\vk.bat Apple Cat Play
I want the final string as " -t Apple -t Cat -t Play"
But I am getting final string as "-t Play" . I am not able to find out that why and how it's overwriting the previous contents of string tcs.
DelayedExpansion-free Solution
Using the call command, we can emulate the delayed expansion. Take a look at this example
call command %%foo%% = setlocal enableDelayedExpansion
command !foo!
According to your case, the code should be changed to this:
SET "tcs="
FOR %%G IN (%*) DO (
CALL SET "tcs=%%tcs%% -t %%G"
)
ECHO %tcs%
Some Buggy Behaviour With CALL
Call doesn't handle redirection characters and some special characters properly:
&
|
<
>
"foo^bar"
The redirection of the first 4 examples won't function as intended, while in the last example, the caret(^) will be doubled. Source: SS64
You have fallen into the delayed expansion trap, like Squashman mentioned.
What Is Delayed Expansion?
In earlier batch files, variables are expanded(changed to it's value) when each line is phrased.
The command processor treats the entire for loop as one command, so the variable %tcs% is treated as nothing. (Because tcs was previously set to nothing.
How To Make Batch Variables Get Expanded At Run-Time?
To preserve compatibility with older batch files, delayed expansion feature is added. To allow the processor to do so, add this line to the start of the batch file:
setlocal enableDelayedExpansion
and we also need to tell the processor which variables to be expanded at run-time, to do so, change the following:
From -> To
%var% -> !var!
Note only %n% variables can be changed to !n!. Metavariables like %%G/%G and %0 cannot be changed.
Here's is the fixed code using delayed expansion.:
SETLOCAL EnableDelayedExpansion
SET "tcs="
FOR %%G IN (%*) DO (
SET "tcs=!tcs! -t %%G"
)
ECHO %tcs%

How to run batch script without using *.bat extension

Is there any method in Windows through which we can execute a batch script without *.bat extension?
This is an interesting topic to me! I want to do some observations about it.
The important point first: A Batch file is a file with .BAT or .CMD extension. Period. Batch files can achieve, besides the execution of usual DOS commands, certain specific Batch-file facilities, in particular:
Access to Batch file parameters via %1 %2 ... and execution of SHIFT command.
Execution of GOTO command.
Execution of CALL :NAME command (internal subroutine).
Execution of SETLOCAL/ENDLOCAL commands.
Now the funny part: Any file can be redirected as input for CMD.exe so the DOS commands contained in it are executed in a similar way of a Batch file, with some differences. The most important one is that previous Batch-file facilities will NOT work. Another differences are illustrated in the NOT-Batch file below (I called it BATCH.TXT):
#echo off
rem Echo off just suppress echoing of the prompt and each loop of FOR command
rem but it does NOT suppress the listing of these commands!
rem Pause command does NOT pause, because it takes the character that follows it
pause
X
rem This behavior allows to put data for a SET /P command after it
set /P var=Enter data:
This is the data for previous command!
echo Data read: "%var%"
rem Complex FOR/IF commands may be assembled and they execute in the usual way:
for /L %i in (1,1,5) do (
set /P line=
if "!line:~0,6!" equ "SHOW: " echo Line read: !line:~6!
)
NOSHOW: First line read
SHOW: Second line
NOSHOW: This is third line
SHOW: The line number 4
NOSHOW: Final line, number five
rem You may suppress the tracing of the execution redirecting CMD output to NUL
rem In this case, redirect output to STDERR to display messages in the screen
echo This is a message redirected to STDERR >&2
rem GOTO command doesn't work:
goto label
goto :EOF
rem but both EXIT and EXIT /B commands works:
exit /B
:label
echo Never reach this point...
To execute previous file, type: CMD /V:ON < BATCH.TXT
The /V switch is needed to enable delayed expansion.
More specialized differences are related to the fact that commands in the NOT-Batch file are executed in the command-line context, NOT the Batch-file context. Perhaps Dave or jeb could elaborate on this point.
EDIT: Additional observations (batch2.txt):
#echo off
rem You may force SET /P command to read the line from keyboard instead of
rem from following lines by redirecting its input to CON device.
rem You may also use CON device to force commands output to console (screen),
rem this is easier to write and read than >&2
echo Standard input/output operations> CON
echo/> CON
< CON set /P var=Enter value: > CON
echo/> CON
echo The value read is: "%var%"> CON
Execute previous file this way: CMD < BATCH2.TXT > NUL
EDIT: More additional observations (batch3.txt)
#echo off
rem Dynamic access to variables that usually requires DelayedExpansion via "call" trick
rem Read the next four lines; "next" means placed after the FOR command
rem (this may be used to simulate a Unix "here doc")
for /L %i in (1,1,4) do (
set /P line[%i]=
)
Line one of immediate data
This is second line
The third one
And the fourth and last one...
(
echo Show the elements of the array read:
echo/
for /L %i in (1,1,4) do call echo Line %i- %line[%i]%
) > CON
Execute this file in the usual way: CMD < BATCH3.TXT > NUL
Interesting! Isn't it?
EDIT: Now, GOTO and CALL commands may be simulated in the NotBatch.txt file!!! See this post.
Antonio
Just use:
type mybat.txt | cmd
Breaking it down...
type mybat.txt reads mybat.txt as a text file and prints the contents. The | says capture anything getting printed by the command on its left and pass it as an input to the command on its right. Then cmd (as you can probably guess) interprets any input it receives as commands and executes them.
In case you were wondering... you can replace cmd with bash to run on Linux.
in my case, to make windows run files without extension (only for *.cmd, *.exe) observed, i have missed pathext variable (in system varailbles) to include .cmd. Once added i have no more to run file.cmd than simply file.
environment variables --> add/edit system variable to include .cmd;.exe (ofcourse your file should be in path)
It could be possible yes, but probably nor in an easy way =) cause first of all.. security.
I try to do the same thing some year ago, and some month ago, but i found no solution about it.. you could try to do
execu.cmd
type toLaunch.txt >> bin.cmd
call bin.cmd
pause > nul
exit
then in toLaunch.txt put
#echo off
echo Hello!
pause > nul
exit
just as example, it will "compile" the code, then it will execute the "output" file, that is just "parse"
instead of parsed you could also just rename use and maybe put an auto rename inside the script using inside toLaunch.txt
ren %0 %0.txt
hope it helped!
It is possible at some degree. You'll need an admin permissions to run assoc and ftype commands. Also a 'caller' script that will use your code:
Lets say the extension you want is called .scr.
Then execute this script as admin:
#echo off
:: requires Admin permissions
:: allows a files with .scr (in this case ) extension to act like .bat/.cmd files.
:: Will create a 'caller.bat' associated with the extension
:: which will create a temp .bat file on each call (you can consider this as cheating)
:: and will call it.
:: Have on mind that the %0 argument will be lost.
rem :: "installing" a caller.
if not exist "c:\scrCaller.bat" (
echo #echo off
echo copy "%%~nx1" "%%temp%%\%%~nx1.bat" /Y ^>nul
echo "%%temp%%\%%~nx1.bat" %%*
) > c:\scrCaller.bat
rem :: associating file extension
assoc .scr=scrfile
ftype scrfile=c:\scrCaller "%%1" %%*
You even will be able to use GOTO and CALL and the other tricks you know. The only limitation is that the the %0 argument will be lost ,tough it can be hardcoded while creating the temp file.
As a lot of languages compile an .exe file for example I think this a legit approach.
If you want variables to be exported to the calling batch file, you could use
for /F "tokens=*" %%g in (file.txt) do (%%g)
This metod has several limitations (don't use :: for comments), but its perfect for configuration files.
Example:
rem Filename: "foo.conf"
rem
set option1=true
set option2=false
set option3=true
#echo off
for /F "tokens=*" %%g in (foo.conf) do (%%g)
echo %option1%
echo %option2%
echo %option3%
pause

batch script - to remove duplicate tokens in file

I have duplicate tokens in text file I would like to create new text file without the duplicate tokens (keeping the delimiters)
The delimiter is:~#^*^#~
example file:
aaa~#^*^#~bbb~#^*^#~aaa~#^*^#~bbb~#^*^#~aaa~#^*^#~bbb~#^*^#~aaa~#^*^#~bbb~#^*^#~aaa~#^*^#~bbb~#^*^#~xxx~#^*^#~bbb~#^*^#~aaa~#^*^#~bbb~#^*^#~aaa~#^*^#~bbb~#^*^#~aaa~#^*^#~bbb~#^*^#~aaa~#^*^#~bbb~#^*^#~aaa~#^*^#~bbb~#^*^#~aaa~#^*^#~bbb~#^*^#~aaa~#^*^#~bbb~#^*^#~aaa~#^*^#~bbb~#^*^#~aaa~#^*^#~bbb
Result should be:
aaa~#^*^#~bbb~#^*^#~xxx
I found script that remove duplicate lines:
==================================
#echo off > outfile
if %1'==' echo which file? && goto :eof
if not exist %1 echo %1 not found && goto :eof
for /f "tokens=* delims= " %%a in (%1) do (
find "%%a" < outfile > nul
if errorlevel 1 echo %%a >> outfile
)
The script work nice for duplicate lines,
So i modified the delims from:
"tokens=* delims="
to
"tokens=* delims=~#^*^#~"
But it wont work, What am i doing wrong? is one of the delimiter characters reserved word?
Thank you for any suggestion.
The FOR DELIMITERS option treats each character as a delimiter. You cannot use a sequence of characters as a delimiter, so it will not help in your case.
Windows batch is a marginal text processor for simple tasks. You have a particularly nasty problem for a Windows batch file. It might be doable, but the code would be complicated and slow at best.
I strongly advise you use some other tool better suited for text processing. I believe any of the following could be used:
VBscript
JavaScript
Powershell
3rd party tools like Gnu sed for Windows, perl, ... many more
Windows batch is probably about the worst choice you could make, especially for your problem. (this is coming from someone who really enjoys using batch)

Escaping an equals sign in DOS batch string replacement command

I need to replace some text in a JNLP file using a DOS batch file to tune it for the local machine.
The problem is that the search pattern contains an equals sign which is messing up the string replacement in the batch file.
I want to replace the line,
<j2se version="1.5" initial-heap-size="100M" max-heap-size="100M"/>
with specific settings for the initial and max heap sizes.
For example at the moment I have,
for /f "tokens=* delims=" %%a in (%filePath%agility.jnlp) do (
set str=%%a
set str=!str:initial-heap-size="100M"=initial-heap-size="%min%M"!
echo !str!>>%filePath%new.jnlp)
but the = in the search pattern is being read as part of the replacement command.
How do I escape the equals sign so it is processed as text?
The best solution is to download and install Cygwin or GNUWin32 but, if you're really limited to the standard command processor, it can get a little messy.
This is not the fastest method in the world but it's at least functional. This command file processes each line one character at a time, treating specially the case where you find the stanza you're looking for.
#echo off
set init=50M
set max=75M
setlocal enableextensions enabledelayedexpansion
for /f "tokens=* delims=" %%a in (agility.jnlp) do (
set str1=%%a
call :morph
echo !str2!>>agility_new.jnlp
echo !str2!
)
endlocal
goto :eof
:morph
set str2=
:morph1
if not "x!str1!"=="x" (
if "!str1:~0,18!"=="initial-heap-size=" (
set str2=!str2!initial-heap-size="!init!"
set str1=!str1:~24!
goto :morph1
)
if "!str1:~0,14!"=="max-heap-size=" (
set str2=!str2!max-heap-size="!max!"
set str1=!str1:~20!
goto :morph1
)
set str2=!str2!!str1:~0,1!
set str1=!str1:~1!
goto :morph1
)
goto :eof
With the input file:
<j2se version="1.5" initial-heap-size="100M" max-heap-size="100M"/>
next line
===
you end up with:
<j2se version="1.5" initial-heap-size="50M" max-heap-size="75M"/>
next line
===
One cannot simply replace (a substring with) an equal-sign, without splitting up (for-statement with "delims==") or trimming…
But perhaps you could go for this simpler but more confusing approach, using the following statement in your for-loop:
set str=!str:"100M" max-heap-size="%min%M" max-heap-size!
It just combines the string to replace with what comes after instead of what comes before, avoiding any equal-sign replacements entirely.
If you can pass the arguments as something else, such as double underscores, you can iterate through them and convert them to '=' in the batch file.
#rem Replace __ with = in batch files.
#rem This works around the lack of equals signs in args
#rem args contains full args string with substitutions in place
setlocal enabledelayedexpansion
:argloop
if "%~1" NEQ "" (
set str=%~1
set out=!str:__==!
set %~1=!out!
set args=!args!!out!
SHIFT
goto :argloop
)
#rem Can now run program on a line on its own with just %args%
Source: https://github.com/mlabbe/batchargs
Here's an alternative solution. If you can afford to download GNU tools, you can use sed:
C:\test>set a=200
C:\test>sed -i.bak "s/^\(.*initial-heap-size=\"\).*\( max.*\)/\1%a%\"\2/" file

Resources