I am trying to write a batch file that will create other, more complex batch files.
This is a portion of my script for right now:
(
echo #echo off
echo for /f "delims=" %%a in (themeset.txt) do set color=%%a&goto setcolor
echo :setcolor
echo color %%color%%
echo pause
) >otherfile.cmd
Theoretically, the output should be a .cmd file with these exact contents:
#echo off
for /f "delims=" %%a in (themeset.txt) do set color=%%a&goto setcolor
:setcolor
color %color%
pause
However, the batch file does not run, and when I attempted to double the %% signs it does not write the entire line correctly.
Any suggestions or solutions would be appreciated.
Max
Magoo's comment beat me to most of the issues.
There is a mostly obscure situation where ECHO text to echo fails, so many people switched to ECHO.text to echo. But somewhere along the way I hit an even more obscure line where that failed, and discovering that semicolon didn't have that problem, I switched to ECHO;text to echo. Which so far, I've not seen any issues.
The FOR /F command has issues, with EOL defaulting to the semicolon being one of them. Some web-page out there made the claim that DELIMS was executed before EOL, so setting EOL to the same as DELIMS would disable it. In my testing it does. But now if we want the whole line, it seems that TOKENS=* disables DELIMS. As far as I know, this is true, but I will admit that I haven't tested that one as much as I would like, so it is probably best to set DELIMS=~, or to similar character that isn't likely to be found in the file.
The % has to be escaped with another percent as in %%, but FOR requires %%, so now you need 4 percents %%%% total.
%%~aa, %%~dd, %%~ff, %%~nn, %%~pp, %%~ss, %%~tt, %%~xx, and %%~zz are all ambiguous. So it probably best to use only capital letters with FOR variables, and probably safest to use only these letters: %%B, %%C, %%E, %%G, %%H, %%I, %%J, %%K, %%L, %%M, %%O, %%Q, %%R, %%U, %%V, %%W, and %%Y See the bottom sections of the help produced by FOR /? for more info on this.
Use the caret to escape special characters. This page, Escape using caret(^), gives some useful info, but its NOT complete. You NEED the caret before the closing parentheses ^), which that page fails to tell you. And to play it safe, I also caret the opening parentheses ^(. But the good thing about that page is that it does points out that when DelayedExpansion is on, you have to double escape the exclamation mark ^^!.
Most of the rest is conventions, I normally capitalize all commands and place a colon in front of all labels, both GOTO :Label and CALL :Label.
The following code:
#ECHO OFF
(
ECHO;#ECHO OFF
ECHO;FOR /F "EOL=~ TOKENS=* DELIMS=~" %%%%L IN ^(themeset.txt^) DO SET Color=%%%%L^&GOTO :SetColor
ECHO;:SetColor
ECHO;Color %%Color%%
ECHO;PAUSE
) >otherfile.cmd
Producted the following file:
#ECHO OFF
FOR /F "EOL=~ TOKENS=* DELIMS=~" %%L IN (themeset.txt) DO SET Color=%%L&GOTO :SetColor
:SetColor
Color %Color%
PAUSE
It is clear from your code that you are not even performing the task in the most efficient manner. The following example will therefore perform the same task, but using another methodology. (i.e. retrieve the first line of the text file content and use it as the color command parameter from another Windows Command Script)
#( Echo #Set /P "colr=" 0^< "themeset.txt"
Echo #Color %%colr%%
Echo #Pause) 1> "otherfile.cmd"
Related
I have a batch script which part of it is clearing the Event logs of Windows 10.
I would like to see the output of the following wevtutil.exe command, but output just on a single line (overwriting each line) instead of many multiple lines.
I know about the ANSI Escape Sequences ESC[1FESC[0J and ESC[1A which can overwrite a previous line (The ESC char is ALT+027 and can be seen in Notepad++), but I haven't been able to figure out how to do that with the wevtutil.exe el command. It still outputs each line one after the other.
Here's what I tried (running in CMD with admin rights):
#echo off
SET OverwriteLine=ESC[1FESC[0J
echo Making sure the variable OverwriteLine actually works. This is line 1.
echo %OverwriteLine%If you see this line instead of line 1, OverwriteLine works.
echo Great, now let^'s see if it works for the "wevtutil.exe cl" command
pause
echo Clearing Event logs...
#echo on
for /F "tokens=*" %%E in ('wevtutil.exe el') DO echo %OverwriteLine% | wevtutil.exe cl "%%E"
pause
I know this can be done via Powershell's $host.ui.RawUI.CursorPosition, but I need a solution for CMD/BAT.
As we deal with a single specific issue per question, and your main one appears to be with the implementation of the VT100 sequences, here is a commented example using a completely different for loop just for demonstration of the technique.
#Echo Off
SetLocal EnableExtensions
Rem Create a variable to use as the escape character
For /F %%G In ('Echo Prompt $E ^| %SystemRoot%\System32\cmd.exe') Do Set "\x1b=%%G"
Rem This is a static line included to demonstrate that it is unaffected
Echo(Output
Rem Turns off the cursor and removes a stray new line caused by Echo
Echo(%\x1b%[?25l%\x1b%[1A
Rem For loop example
For /L %%G In (1,1,8) Do (
Rem Clears to the beginning of the line, prints the output, and moves up a line
Echo(%\x1b%[1KLine %%G%\x1b%[1A
Rem Creates a short delay between output lines to see the effect
%SystemRoot%\System32\PATHPING.EXE 127.0.0.1 -n -q 1 -p 650 1>NUL
)
Rem Turns on the cursor again
Echo(%\x1b%[?25h
Pause
OK, found the solution:
for /F "tokens=*" %%E in ('wevtutil.exe el') DO (wevtutil.exe cl "%%E" | echo <ESC>[1F<ESC>[0KClearing %%E)
Explanation/Notes:
< ESC> means the special ESC escape code sequence. You can generate this char by typing ALT+027 in Notepad++ if you're editing your code in there, or generate it at runtime using the FOR loop that Compo mentioned.
We move the cursor to the beginning of the previous line with ESC[1F.
We then clear from the cursor to the end of the line with ESC[0K.
Clearing the Windows event logs requires running the CMD script with Administrator rights.
Expect some event logs to fail. The failed ones will remain on the screen, each on a new line (which might become handy). If you don't want to see any failures, just add 2>nul : DO (wevtutil.exe cl "%%E" 2>nul | echo <ESC>[1F<ESC>[0KClearing %%E)
You can learn more about escape codes here.
I'm new to Windows cmd and .bat, and to Tesseract. But thanks to this list I've managed a couple of successes.
My first success was this cmd-window line:
tesseract.exe -l eng+lat+ita D:\TIFs\Convivio.tiff D:\TIFs\Convivio
My next success was the .bat file:
:Start
#Echo off
ECHO.
ECHO This is a batch file
ECHO.
PAUSE
BREAK=ON
Set _SourcePath=D:\temp\TIFs\*.tif
Set _OutputPath=D:\temp\TIFs\
Set _Tesseract="D:\temp\Tesseract-OCR\tesseract.exe"
:Convert
For %%A in (%_SourcePath%) Do Echo Converting "%%A"...... &"D:\temp\Tesseract-OCR\tesseract.exe" "%%A" "%_OutputPath%%%~nA"
PAUSE
:End
Set "_SourcePath="
Set "_OutputPath="
Set "_Tesseract="
The problem now is how to include in the .bat file that"-l eng+lat+ita" bit from the cmd-window line.
I got the idea that this is possible from an explanation of the "For" command, which states that "do command" can be followed by "CommandLineOptions" (i.e., "-l eng+lat+ita").
Any help would be much appreciated... 'cause I've been banging my head on this one for hours now...
UPDATE: Found an alternative, but still would like an answer to my question.
I didn't know that "FOR" commands could be run from cmd. So, I pasted the folllowing line in the cmd window:
for %i in (*.tif) do "D:\temp\Tesseract-OCR\tesseract.exe" -l eng+lat+ita "%i" "D:\temp\%~ni"
And, it worked!
As I say, though, how to do this with a .bat file?
#ECHO OFF
SETLOCAL
:Start
#Echo off
ECHO.
ECHO This is a batch file
ECHO.
PAUSE
BREAK=ON
Set "_SourcePath=D:\temp\TIFs\*.tif"
Set "_OutputPath=D:\temp\TIFs"
Set "_Tesseract=D:\temp\Tesseract-OCR\tesseract.exe"
:Convert
For %%A in ("%_SourcePath%") Do Echo Converting "%%A"...... &"%_Tesseract%" -l eng+lat+ita "%%A" "%_OutputPath%\%%~nA"
PAUSE
:End
rem Set "_SourcePath="
rem Set "_OutputPath="
rem Set "_Tesseract="
GOTO :EOF
Since I don't have the tesseract utility, I used another. The above worked for me as I expected with that other utility, so no guarantees with tesseract.
It's normal practice to start a batch with setlocal which makes the clean-up effort unnecessary (hence remmed-out) since an implicit endlocal is executed when the batch terminates, restoring the environment to its initial state.
Assigning values containing quotes is valid but awkward when combining elements. Ditto terminating a value with a backslash. I've converted your code to my preferred syntax. Note that the syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned.
Will it work in your situation? Over to you to try.
i am trying to code a simple script in batch that can find and replace a line
so far, I've found a snippet that works perfectly fine for my purpose the only problem is that it removes empty lines
and i can't figure out why!!
I've tried to add another if statement in this for loop but I fail
also I found that there is a bat called JREPL, i tried to run few simple commands from the docs and i failed again XD
here is the snippet:
:Variables
set InputFile=t.txt
set OutputFile=t-new.txt
set _strFind= old "data"
set _strInsert= new "data";
:Replace
>"%OutputFile%" (
for /f "usebackq delims=" %%A in ("%InputFile%") do (
if "%%A" equ "%_strFind%" (echo %_strInsert%) else (echo %%A)
)
)
i was expecting that this snippet won't remove my empty lines
and i can't figure out why
I am posting this without testing, as I do not have the environment to test as we speak.
But to explain your issue, cmd will ommit empty lines as it is built that way. It is the same as setting a variable to nothing and expecting it to return a result, so we simply assign values to each line by sort of simulating a detection of line breaks (Don't know exactly how to explain that one) but nevertheless, we will add some additional characters to the lines to ensure we get line breaks, the just get rid of them once we have them, So here goes:
#echo off
setlocal enabledelayedexpansion
set inputfile=t.txt
set outputfile=t-new.txt
set _strfind=old "data"
set _strinsert=new "data";
for /f "tokens=*" %%a in ('type "%inputfile%" ^| find /v /n "" ^& break ^> "%inputfile%"') do (
set "str=%%a"
set "str=!str:*]=!"
if "!str!"=="%_strfind%" set "str=%_strinsert%"
>>%outputfile% echo(!str!
)
That should send to output file.. You can however make the output file the same as the input as it would then be the same as replacing the text inline in the original file. Once I am able to test, I will fix the answer if there are any issues with it.
As a side note, be careful of where you have additional whitespace in your variables you set. For instance:
set a = b
has 2 issues, the variable, containing a space after a will be created with the space. So it will be seen as:
%a %
The aftermath of this is that the value of the variable will start with a leading space, so when you expected b as the value, it in fact became b
Then lastly, it is alsways a good idea to enclose your variables with double quotes, simply again to eliminate whitespace, because:
set a=b
Even though you cannot see it with your naked eyes, contains a space at the end, so doing a direct match like:
if "b"=="b"
Will result in a false statement as in fact we have:
if "b"=="b "
So the correct statement would be to set variables as:
set "a=b"
if "%a%"=="b"
which will be a perfect match.
Note I posted this from my phone, so any spelling, grammar and code issues I will resolved as I go though my answer.
…and one way using JREPL
JRepl "old \qdata\q" "new \qdata\q;" /I /XSEQ /F "t.txt" /O "t-new.txt"
Sorry, for bothering you for the (n+1)th time about search & replace with batch scripts.
I have text files (actually PS-files) (approx. 10kB-3MB) where I need to replace just a few numbers.
This should be easy, I thought.
I found quite a few scripts here on Stackoverflow but none of them worked properly so far. If I have overlooked THE "working one" please let me know.
The last one I tried:
#echo off
setlocal DisableDelayedExpansion
set OutputFile=%1
set OutputFile=%OutputFile:"=%
set InputFile=%OutputFile%.tmp
set SearchString=636170656C6C6133
set ReplaceString=636170656C6C6134
rem write empty file
type NUL > %OutputFile%
for /f "tokens=1,* delims=¶" %%A in ( '"type %InputFile%"') do (
SET string=%%A
setlocal EnableDelayedExpansion
SET modified=!string:%SearchString%=%ReplaceString%!
echo !modified!>>%OutputFile%
endlocal
)
del %InputFile%
First of all, it seems to be pretty(!) slow. I can see on disk how the file size increases.
The occurrences of the numbers seem to be replaced. However, the file is altered, which I easily can see from the different file size. As far as I can see, empty lines, exclamation marks and lines beginning with semicolon are skipped. This is messing up my file completely.
How to avoid this?
If I do the same thing with Perl I really get only the numbers altered, nothing else. However, I don't want to and cannot use Perl. I also don't want to use other extra programs or Windows-Powershell, since it should work on older systems too.
Is there any way to achieve this with a simple Windows batch script?
Thanks!
I believe the following is a working bat script that should not make any changes other than the desired number change:
#echo off
setlocal DisableDelayedExpansion
set "out=%~1"
set "in=%out%.tmp"
set "find=636170656C6C6133"
set "repl=636170656C6C6134"
>"%out%" (
for /f "delims=" %%A in ('findstr /n "^" "%in%"') do (
set "str=%%A"
setlocal EnableDelayedExpansion
set "str=!str:*:=!"
if defined str set "str=!str:%find%=%repl%!"
echo(!str!
endlocal
)
)
del "%in%"
Changes I have made:
Use %~1 to remove enclosing parentheses. Though technically, that is not necessary. Something like echo test >"someName.txt".new will work just fine.
FOR /F strips empty lines. I used FINDSTR to prefix each line with the line number, followed by a colon. Now there are no empty lines.
I use an extra variable expansion find/replace with * to remove the line number prefix.
Variable expansion find/replace will fail if a string is empty (undefined variable). So I verify the variable is defined before doing find/replace.
ECHOing an empty line, or line containing only white space, will result in ECHO is off. output. This is solved by using echo(
It takes time to initialize redirection, and your loop does this every iteration, which slows things down. I improved performance by enclosing the entire FOR loop in parentheses and redirecting only once.
You still may see a slight file size change for any of the following reasons
If the input has \n line terminators instead of \r\n.
If the last line of input is not terminated by \r\n. The script terminates all lines with \r\n, regardless what the input had.
The script will fail if any line contains a null byte, or if any line is >~8k length.
I hate editing text files with batch - it is complicated code, slow, and even the best possible solution still has significant limitations.
I recommend you use JREPL.BAT - a command line regular expression text processing utility. JREPL is pure script (hybrid batch/JScript) that runs natively on any Windows machine from XP onward - no 3rd party exe file or special configuration is needed.
The tool is very powerful, with many options. Full documentation is available from the command line via jrepl /?, or jrepl /?? for paged help.
Solving your problem with JREPL is trivial - you don't even need another script. The following command will work right from a command prompt:
jrepl 636170656C6C6133 636170656C6C6134 /f input.txt /o output.txt
Use CALL JREPL if you put the command within another batch script.
JREPL is way more powerful than what you need for this simple problem. But it is incredibly convenient, and once you have the utility, I suspect you will find many uses for it. Especially if you learn to use regular expressions, as well as the many JREPL options.
A VBS script
Set Inp = WScript.Stdin
Set Outp = Wscript.Stdout
Text = Inp.readall
Text = Replace(Text, "636170656C6C6133", "636170656C6C6134")
outp.write Text
To use
cscript //nologo script.vbs < input.txt > Output.txt
You use the right tool for the job. Batch is for starting programs and copying files.
The above is suited to the file sizes given. However if we are getting up to 100s of MB then this code is better.
Set Inp = WScript.Stdin
Set Outp = Wscript.Stdout
Do Until Inp.AtEndOfStream
Text = Inp.readline
Text = Replace(Text, "636170656C6C6133", "636170656C6C6134")
outp.writeline Text
Loop
If I get my parameter with %1 and it is "Server" how can I add a + sign after every letter?
So my result would be "S+e+r+v+e+r"?
I think Batch file to add characters to beginning and end of each line in txt file this is a similar question but I don't know how to change the code for this purpose.
Any help would be great!
I'm pretty sure this has been asked and answered before, but I couldn't find it.
There is a really cool (and fast) solution that I saw posted somewhere. It uses a new cmd.exe process with the /U option so output is in unicode. The interesting thing about the unicode is that each ASCII character is represented as itself followed by a nul byte (0x00). When this is piped to MORE, it converts the nul bytes into newlines!. Then a FOR /F is used to iterate each of the characters and build the desired string. A final substring operation is used to remove the extra + from the front.
I tweaked my memory of the code a bit, playing games with escape sequences in order to get the delayed expansion to occur at the correct time, and to protect the character when it is appended - all to get the technique to preserve ^ and ! characters. This may be a new twist to existing posted codes using this general technique.
#echo off
setlocal enableDelayedExpansion
set "str=Server bang^! caret^^"
set "out="
for /f delims^=^ eol^= %%A in ('cmd /u /v:on /c echo(^^!str^^!^|more') do set "out=!out!+^%%A"
set "out=!out:~1!"
echo Before: !str!
echo After: !out!
--OUTPUT---
Before: Server bang! caret^
After: S+e+r+v+e+r+ +b+a+n+g+!+ +c+a+r+e+t+^
This batch file should do it:
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET Text=%~1
SET Return=
REM Batch files don't have a LEN function.
REM So this loop will process up to 100 chars by doing a substring on each.
FOR /L %%I IN (0,1,100) DO (
CALL SET Letter=!Text:~%%I,1!
REM Only process when a letter is returned.
IF NOT "!Letter!" == "" (
SET Return=!Return!+!Letter!
) ELSE (
REM Otherwise, we have reached the end.
GOTO DoneProcessing
)
)
:DoneProcessing
REM Remove leading char.
SET Return=%Return:~1,999%
ECHO %Return%
ENDLOCAL
Calling with Test.bat Server prints S+e+r+v+e+r to the console.