I'm attempting to rewrite a configuration file using a Windows Batch file.
I'm looping through the lines of the file and looking for the line that I want to replace with a specified new line.
I have a 'function' that writes the line to the file
:AddText %1 %2
set Text=%~1%
set NewLine=%~2%
echo "%Text%" | findstr /C:"%markerstr%" 1>nul
if errorlevel 1 (
if not "%Text%" == "" (
setlocal EnableDelayedExpansion
(
echo !Text!
) >> outfile.txt
) else (
echo. >> outfile.txt
)
) else (
set NewLine=%NewLine"=%
setlocal EnableDelayedExpansion
(
echo !NewLine!
) >> outfile.txt
)
exit /b
The problem is when %Text% is a string with embedded double quotes.
Then it fails. Possibly there are other characters that would cause it to fail too.
How can I get this to be able to work with all text found in the configuration file?
Try replacing all " in Text with ^".
^ is escape character so the the " will be treated as regular character
you can try the following:
:AddText %1 %2
set _Text=%~1%
set Text=%_Text:"=^^^"%
... rest of your code
REM for example if %1 is "blah"blah"blah"
REM _Text will be blah"blah"blah
REM Text will be blah^"blah^"blah
Other characters that could cause you errors (you can solve it with the above solution) are:
\ & | > < ^
In a windows batch shell (command) double quote are escape with ^ on standard command line BUT with a double double quote inside a double quoted string
echo Hello ^"Boy^"
echo "Hello ""Boy"""
(remark: second line will produce the external surrounding double quote in the output also)
Related
I'm trying to print special characters to a text file.
Those are my problematic lines :
(
echo FOR /L %%A IN (1,1,3) DO (
echo echo "%~f0" myText
echo )
) > myFile.bat
I tried to escape the special characters with ^ and with \ but seems like it's not working properly
I tried :
1.
(echo FOR \/L \%\%A IN (1,1,3) DO \() >> myFile.bat
2.
(echo FOR ^/L ^%^%A IN (1,1,3) DO ^() >> myFile.bat
3.
(echo echo \"%~f0\" myText) >> myFile.bat
4.
(echo echo ^"%~f0\^" myText) >> myFile.bat
None of them is working properly.
I was wondering if there is a command like in php
$myText = <<<EOF
EOF;
That would magically do the task but if not ...
This is all you need to do:
(
echo FOR /L %%%%A IN (1,1,3^) DO (
echo echo "%%~f0" myText
echo ^)
)>myFile.cmd
Escape the intenal closing parentheses with the standard circumflex character, escape each percent with another.
why the following batch script is failing with "The syntax of the command is incorrect." error on Windows 7 if I provide no argument:
IF NOT [%1]==[] (
echo "blablabla" > %1
) ELSE (
echo "please provide argument"
)
But there is no problem with this code:
IF NOT [%1]==[] (
echo "blablabla"
) ELSE (
echo "printing not existing argument: %1"
)
Thanks in advance
Encapsulating the filename by double quotations enforces the if/else block to parse and read the expression correctly.
For example
IF NOT [%1]==[] (
echo "blablabla" > "%1"
) ELSE (
echo "please provide argument"
)
The problem is that the entire if/else block is read and parsed at once. So if %1 is empty, the redirection > is still parsed and evaluated invalid.
You can work around the issue like that:
if not "%~1"=="" (
goto :Redirect
) else (
echo please provide argument
)
goto :Continue
:Redirect
> "%~1" echo blablabla
:Continue
I replaced the brackets by quotation marks, because they protect white-spaces and other special characters. The ~-symbol ensures that the returned argument appears unquoted, so there is always one pair of quotes in the expression "%~1".
Moreover, I removed quotes from the echo command lines as they were output too, and I moved the redirection part > "%~1" in front of echo in order to avoid a trailing SPACE to be returned.
How do we compare strings which got space and special chars in batch file?
I am trying:
if %DevEnvDir% == "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\"(
echo VS2010
)
But it gives an error "Files was unexpected at this time."
I tried:
if "%DevEnvDir%" == "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\"(
echo VS2010
)
But it gives an error "The syntax of the command is incorrect."
Any ideas?
Just put quotes around the Environment variable (as you have already done):
if "%DevEnvDir%" == "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\"
The strings you are comparing are fine, the problem is the way you put the opening bracket without a space. That is confusing it.
Works for me...
C:\if "%gtk_basepath%" == "C:\Program Files\GtkSharp\2.12\" (echo yes)
yes
While #ajv-jsy's answer works most of the time, I had the same problem as #MarioVilas. If one of the strings to be compared contains a double quote ("), the variable expansion throws an error.
Example:
#echo off
SetLocal
set Lhs="
set Rhs="
if "%Lhs%" == "%Rhs%" echo Equal
Error:
echo was unexpected at this time.
Solution:
Enable delayed expansion and use ! instead of %.
#echo off
SetLocal EnableDelayedExpansion
set Lhs="
set Rhs="
if !Lhs! == !Rhs! echo Equal
:: Surrounding with double quotes also works but appears (is?) unnecessary.
if "!Lhs!" == "!Rhs!" echo Equal
I have not been able to break it so far using this technique. It works with empty strings and all the symbols I threw at it.
Test:
#echo off
SetLocal EnableDelayedExpansion
:: Test empty string
set Lhs=
set Rhs=
echo Lhs: !Lhs! & echo Rhs: !Rhs!
if !Lhs! == !Rhs! (echo Equal) else (echo Not Equal)
echo.
:: Test symbols
set Lhs= \ / : * ? " ' < > | %% ^^ ` ~ # # $ [ ] & ( ) + - _ =
set Rhs= \ / : * ? " ' < > | %% ^^ ` ~ # # $ [ ] & ( ) + - _ =
echo Lhs: !Lhs! & echo Rhs: !Rhs!
if !Lhs! == !Rhs! (echo Equal) else (echo Not Equal)
echo.
A roundabout solution use a subroutine
CALL :Comparator %var1% %var2% <- or the string you want to compare
IF %retVal%==1 (
do stuff
) ELSE (
do other things
)
GOTO :eof
:Comparator
IF "%~1" == "%~2" (set retVal=1) ELSE (set retVal=0)
GOTO :eof
It won't work if there is double-quotes inside the strings though, but if you compare file paths, there shouldn't be.
%~[1-9] the tilde '~' removes double quotes around the variable, and then you put new ones around them if the strings has spaces inside. The tilde trick only works with passed variable though, hence the subroutine.
The solution is DO NOT USE SPACES!
IF "%DevEnvDir%"=="C:\" (
I have a set of data like this
7859 10000:00 7859 10000:00 (xfer#1, to-check=1033/1035)
32768 000:17 22174479 10000:00 (xfer#2, to-check=1032/1035)
They are read from a file and passed line by line to a method inside my batch script
What I want to do in that method is to extract only
7859
22174479
from this lines, basically whatever is after "\d+:\d\d\s+", then what follows are the numbers that I need and then another "\d\d.*"
Is this possible using only batch script regular expression and search and replace?
I tried and read a bunch of articles but could not find a solution
In the and I want to add the numbers
Thank you
EDIT
Based on Andrei's comment to David Ruhmann's answer, Andrei wants the token that is 2 positions before (xfer#, not the 3rd token from the beginning.
Do note that batch is not the best language to use for regex! Cmd processes the input one line at a time, whereas regex allows for multi-line processing.
It sounds like you just need to perform a token grab from the lines. Assuming the more complete regex for the line looks like this [\d+\s+\d+:\d\d\s+]+\(xfer#\d+, to-check=\d+/\d+\).
This allows us to know that there are constant delimiters in the line. : colons, and \s+ whitespace. From there it is just a matter of using those anchors to determine the token position.
Extract the third token delimited by single line whitespace from the line.
for /f "tokens=3" %%A in ("line") do echo %%A
Extract the second token delimited by single line whitespace from the second token delimited by colons from the line.
for /f "tokens=2 delims=:" %%A in ("line") do (
for /f "tokens=2" %%B in ("%%A") do echo %%B
)
Update
Extract the second token before the last colon.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "Line=32768 004:47 2686976 2200:03 11707819 10000:01 (xfer#5264, to-check=1020/6975)"
set "Last="
for /f "delims=" %%A in ('echo("%Line::="^&echo("%"') do (
for /f "tokens=2" %%B in ("%%A") do (
if defined This set "Last=!This!"
set "This=%%B"
)
)
echo %Last%
endlocal
pause >nul
Limitations
Lines containing an odd number of double quotation marks " will cause the script to crash. One method to prevent this is to strip out the quotations before the for loop with set Line=%Line:"=%.
Based on your comment to David Ruhmann's answer, you want the token that is 2 positions before the (xfer# string. I suppose it can be done using native batch commands, but that is a nasty problem.
I am assuming you are restricted to commands that are native to Windows - no downloaded executables.
I'm hoping that you may use JScript, since it is native to Windows.
I have written a hybrid JScript/Batch utility script named "REPL.BAT" that performs regex search and replace. It is an amazingly useful utility, despite not requiring much code. The utility makes the solution very simple.
I use FINDSTR to filter out the lines that don't meet the template of at least 2 space delimted tokens prior to (xfer#. I pipe those results to my REPL utility and preserve only the desired token. The result is sent to stdout.
findstr /r /c:" [^ ][^ ]* [^ ][^ ]* (xfer#" test.txt | repl ".* ([^ ]+) ([^ ]+) \(xfer#.*" "$1"
Here is the code for the REPL.BAT utility script. Full documentation is embedded within the script.
#if (#X)==(#Y) #end /* Harmless hybrid line that begins a JScript comment
::************ Documentation ***********
:::
:::REPL Search Replace [Options [SourceVar]]
:::REPL /?
:::
::: Performs a global search and replace operation on each line of input from
::: stdin and prints the result to stdout.
:::
::: Each parameter may be optionally enclosed by double quotes. The double
::: quotes are not considered part of the argument. The quotes are required
::: if the parameter contains a batch token delimiter like space, tab, comma,
::: semicolon. The quotes should also be used if the argument contains a
::: batch special character like &, |, etc. so that the special character
::: does not need to be escaped with ^.
:::
::: If called with a single argument of /? then prints help documentation
::: to stdout.
:::
::: Search - By default this is a case sensitive JScript (ECMA) regular
::: expression expressed as a string.
:::
::: JScript syntax documentation is available at
::: http://msdn.microsoft.com/en-us/library/ae5bf541(v=vs.80).aspx
:::
::: Replace - By default this is the string to be used as a replacement for
::: each found search expression. Full support is provided for
::: substituion patterns available to the JScript replace method.
::: A $ literal can be escaped as $$. An empty replacement string
::: must be represented as "".
:::
::: Replace substitution pattern syntax is documented at
::: http://msdn.microsoft.com/en-US/library/efy6s3e6(v=vs.80).aspx
:::
::: Options - An optional string of characters used to alter the behavior
::: of REPL. The option characters are case insensitive, and may
::: appear in any order.
:::
::: I - Makes the search case-insensitive.
:::
::: L - The Search is treated as a string literal instead of a
::: regular expression. Also, all $ found in Replace are
::: treated as $ literals.
:::
::: E - Search and Replace represent the name of environment
::: variables that contain the respective values. An undefined
::: variable is treated as an empty string.
:::
::: M - Multi-line mode. The entire contents of stdin is read and
::: processed in one pass instead of line by line. ^ anchors
::: the beginning of a line and $ anchors the end of a line.
:::
::: X - Enables extended substitution pattern syntax with support
::: for the following escape sequences:
:::
::: \\ - Backslash
::: \b - Backspace
::: \f - Formfeed
::: \n - Newline
::: \r - Carriage Return
::: \t - Horizontal Tab
::: \v - Vertical Tab
::: \xnn - Ascii (Latin 1) character expressed as 2 hex digits
::: \unnnn - Unicode character expressed as 4 hex digits
:::
::: Escape sequences are supported even when the L option is used.
:::
::: S - The source is read from an environment variable instead of
::: from stdin. The name of the source environment variable is
::: specified in the next argument after the option string.
:::
::************ Batch portion ***********
#echo off
if .%2 equ . (
if "%~1" equ "/?" (
findstr "^:::" "%~f0" | cscript //E:JScript //nologo "%~f0" "^:::" ""
exit /b 0
) else (
call :err "Insufficient arguments"
exit /b 1
)
)
echo(%~3|findstr /i "[^SMILEX]" >nul && (
call :err "Invalid option(s)"
exit /b 1
)
cscript //E:JScript //nologo "%~f0" %*
exit /b 0
:err
>&2 echo ERROR: %~1. Use REPL /? to get help.
exit /b
************* JScript portion **********/
var env=WScript.CreateObject("WScript.Shell").Environment("Process");
var args=WScript.Arguments;
var search=args.Item(0);
var replace=args.Item(1);
var options="g";
if (args.length>2) {
options+=args.Item(2).toLowerCase();
}
var multi=(options.indexOf("m")>=0);
var srcVar=(options.indexOf("s")>=0);
if (srcVar) {
options=options.replace(/s/g,"");
}
if (options.indexOf("e")>=0) {
options=options.replace(/e/g,"");
search=env(search);
replace=env(replace);
}
if (options.indexOf("l")>=0) {
options=options.replace(/l/g,"");
search=search.replace(/([.^$*+?()[{\\|])/g,"\\$1");
replace=replace.replace(/\$/g,"$$$$");
}
if (options.indexOf("x")>=0) {
options=options.replace(/x/g,"");
replace=replace.replace(/\\\\/g,"\\B");
replace=replace.replace(/\\b/g,"\b");
replace=replace.replace(/\\f/g,"\f");
replace=replace.replace(/\\n/g,"\n");
replace=replace.replace(/\\r/g,"\r");
replace=replace.replace(/\\t/g,"\t");
replace=replace.replace(/\\v/g,"\v");
replace=replace.replace(/\\x[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}/g,
function($0,$1,$2){
return String.fromCharCode(parseInt("0x"+$0.substring(2)));
}
);
replace=replace.replace(/\\B/g,"\\");
}
var search=new RegExp(search,options);
if (srcVar) {
WScript.Stdout.Write(env(args.Item(3)).replace(search,replace));
} else {
while (!WScript.StdIn.AtEndOfStream) {
if (multi) {
WScript.Stdout.Write(WScript.StdIn.ReadAll().replace(search,replace));
} else {
WScript.Stdout.WriteLine(WScript.StdIn.ReadLine().replace(search,replace));
}
}
}
:: Does %variable% =~ s/old/new/
setlocal ENABLEDELAYEDEXPANSION
for /f "delims=" %%a in ('echo !variable! ^|perl -pe "s/regexp/replace/" ') do set variable=%%a
The easiest and most flexible way to accomplish what you want would be to use awk (regexp examples) or sed (for example: sed -i -r -e "s/(\d+:\d\d\s+)\d+/\1replacementstring/g" filename) from GnuWin32, which both support Perl regexp syntax. I think what you're involved in is exactly what awk was designed for.
If you're stuck using only what's available without having to use 3rd party tools, you can perform regexp matches using vbscript. You can call vbscript by echoing the script to a .vbs file, calling cscript vbsfile, and capturing its output. Here's a proof of concept.
#echo off & setlocal enabledelayedexpansion
:: rxp.bat
:: rxp /? for usage instructions
if #%4==# goto usage
set global=false
set replace=false
for %%I in (%*) do (
if not #!next!==# (
if !next!==string set string=%%I
if !next!==pattern set pattern=%%I
if !next!==replace set replace=%%I
set next=
)
if #%%I==#/s set next=string
if #%%I==#/p set next=pattern
if #%%I==#/r set next=replace
if #%%I==#/g set global=true
)
if #%string==# goto usage
if #%pattern==# goto usage
set string=!string:"=""!
set string=!string:\=!
set pattern=!pattern:"=""!
set pattern=!pattern:\=!
if #!replace!==#false (
call :rxp !string:~1,-1! !pattern:~1,-1! !global!
) else (
set replace=!replace:"=""!
set replace=!replace:\=!
call :rxp !string:~1,-1! !pattern:~1,-1! !global! !replace:~1,-1!
)
goto :EOF
:rxp string pattern global replacement
echo Set rxp = New RegExp>regexp.vbs
echo rxp.Pattern = %2>>regexp.vbs
echo rxp.Global = %3>>regexp.vbs
if #%4==# (
echo Set res = rxp.Execute^(%1^)>>regexp.vbs
echo For Each match in res>>regexp.vbs
echo Wscript.Echo match.value>>regexp.vbs
echo Next>>regexp.vbs
) else (
echo Wscript.echo rxp.Replace^(%1, %4^)>>regexp.vbs
)
cscript /nologo regexp.vbs
del /q regexp.vbs
goto :EOF
:usage
echo Usage: %~nx0 /s "string" /p "regexp" [/g] [/r "replacement text"]
echo;
echo /s -- search string
echo;
echo /p -- regular expression pattern
echo Example: /p "<[^>]+>" to search for markup tags
echo matches ^<span class='a'^> or similar
echo;
echo /r -- replacement text (optional)
echo If specified, replace the matched text
echo Example: /p "(<div class=')blue('>)" /r "$1red$2"
echo matches ^<div class='blue'^>
echo replaces match with ^<div class='red'^>
echo;
echo /g -- global match (optional)
echo match every occurrence (matches only the first by default)
echo;
echo notes: If the regexp pattern includes capturing parentheses, use ^$1-^$9 as
echo backreferences in your replacement text. If any of your strings include
echo quotation marks, they can be escaped with a backslash (\).
echo;
echo Example:
echo %~nx0 /s "text begin <div id=\"foo\"> text end" /p "(<div)[^>]+(>)"
echo /r "$1 class=\"bar\"$2"
echo;
echo matches ^<div id="foo"^>, replaces match with ^<div class="bar"^>
echo output: text begin ^<div class="bar"^> text end
example output:
C:\Users\me\Desktop>rxp /s "7859 10000:00 7849 10000:00 (xfer#1, to-check=1033/1035)" /p "(\d+:\d\d\s+)\d+" /r "$1foo"
7859 10000:00 foo 10000:00 (xfer#1, to-check=1033/1035)
C:\Users\me\Desktop>rxp
Usage: rxp.bat /s "string" /p "regexp" [/g] [/r "replacement text"]
/s -- search string
/p -- regular expression pattern
Example: /p "<[^>]+>" to search for markup tags
matches <span class='a'> or similar
/r -- replacement text (optional)
If specified, replace the matched text
/g -- global match (optional)
match every occurrence (matches only the first by default)
notes: If the regexp pattern includes capturing parentheses, use $1-$9 as
backreferences in your replacement text. If any of your strings include
quotation marks, they can be escaped with a backslash (\).
Example:
rxp.bat /s "text begin <div id=\"foo\"> text end" /p "(<div)[^>]+(>)"
/r "$1 class=\"bar\"$2"
matches <div id="foo">, replaces match with <div class="bar">
output: text begin <div class="bar"> text end
I have the following string inside my Windows batch file:
"-String"
The string also contains the twoe quotation marks at the beginning and at the end of the string, so as it is written above.
I want to strip the first and last characters so that I get the following string:
-String
I tried this:
set currentParameter="-String"
echo %currentParameter:~1,-1%
This prints out the string as it should be:
-String
But when I try to store the edited string like this, it fails:
set currentParameter="-String"
set currentParameter=%currentParameter:~1,-1%
echo %currentParameter%
Nothing gets printed out. What do I do wrong?
This really is strange. When I remove the characters like this it works:
set currentParameter="-String"
set currentParameter=%currentParameter:~1,-1%
echo %currentParameter%
it prints out:
-String
But actually my batch is a bit more complicated and there it does not work. I will show what I programmed:
#echo off
set string="-String","-String2"
Set count=0
For %%j in (%string%) Do Set /A count+=1
FOR /L %%H IN (1,1,%COUNT%) DO (
echo .
call :myFunc %%H
)
exit /b
:myFunc
FOR /F "tokens=%1 delims=," %%I IN ("%string%") Do (
echo String WITHOUT stripping characters: %%I
set currentParameter=%%I
set currentParameter=%currentParameter:~1,-1%
echo String WITH stripping characters: %currentParameter%
echo .
)
exit /b
:end
And the output is:
.
String WITHOUT stripping characters: "-String"
String WITH stripping characters:
.
.
String WITHOUT stripping characters: "-String2"
String WITH stripping characters: ~1,-1
.
But what i want is:
.
String WITHOUT stripping characters: "-String"
String WITH stripping characters: -String
.
.
String WITHOUT stripping characters: "-String2"
String WITH stripping characters: -String2
.
Hope this will help you.
setlocal enabledelayedexpansion
echo String WITHOUT stripping characters: %%I
set currentParameter=%%I
set currentParameter=!currentParameter:~1,-1!
echo String WITH stripping characters: !currentParameter!
You are modyfing a variable inside a parenthesized block. Watch out - the new value will not be used within the same block (unless you delimit the variable with ! instead of % - and running in the enabledelayedexpansion mode).
Or just extract the couple of lines into another sub-function, using a plain sequence of lines insted of ( )
greets, Stach
This script takes advantage of ENABLEDELAYEDEXPANSION.
If you don't know, batch scripts execute for and if commands all in one; hence if you do:
if true==true (
#echo off
set testvalue=123
echo %testvalue%
pause >NUL
)
You wont output anything, because when echo %testvalue% is executed, it has not recognized the testvalue has been changed.
Using delayedexapnsion allows the script to read that value as it is now, and forget the problem I stated before. You use it just like %testvalue%, but you may do !testvalue! to fix this:
if true==true (
#echo off
set testvalue=123
echo !testvalue!
pause >NUL
)
Would echo 123.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set string="-String","-String2"
Set count=0
For %%j in (%string%) Do Set /A count+=1
FOR /L %%H IN (1,1,%COUNT%) DO (
echo .
call :myFunc %%H
)
exit /b
:myFunc
FOR /F "tokens=%1 delims=," %%I IN ("%string%") Do (
echo String WITHOUT stripping characters: %%I
set currentParameter=%%I
set currentParameter=!currentParameter:~1,-1!
echo String WITH stripping characters: !currentParameter!
echo .
)
exit /b
:end
~ Alex
I had similar problem but it solved by removing spaces between
Ex : set FileName=%Name:~0,11% # Working as No space before and after '='
Ex : set FileName = %Name:~0,11% # Not Working as space before OR after '='
So please try by remove spaces, It should work
Note: command line should be reopen to get refresh the background values else it shows the same output as it is stored in temp