(Edited question for clarity)
Applying substring operation to a token value in a FOR does not work:
for /F "tokens=1 delims=" %%G in ("tokenvalue ") do (
Echo %%G:~-1
)
The substring operation does not delete the space at the end. Instead, :~-1 gets appended to the echoed result to produce:
tokenvalue :~-1
I cannot reproduce your problem here. Only when I append a space to the input file it also appears in the output file.
If you do
echo %%G >> D:\newfile.txt
then a space gets appended, obviously. This might be the case if you simplified your code before posting here.
If you indeed start out with a space in the input, then use the following:
setlocal enabledelayedexpansion
for /f "tokens=1 delims=" %%G in (D:\originalFile.txt) do (
set "line=%%G"
echo !line:~-1!>>D:\newfile.txt
)
You apply substring operations only to environment variables as the help already states.
In any case, if you're sure that the input file does not contain the trailing space, then you don't actually need the loop. A simple
type D:\originalFile.txt >> D:\newfile.txt
should suffice.
Related
I have a parameter string read from a properties file. One of the properties is as below:
"CUSTOM_JAVA_OPTIONS=-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080"
I need to split this string on the first occurrence of "=" and set a parameter with the value:
-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080
I am trying to split the string first on the = token and then remove the fist sub-string token from the original string.
In the below code %%G will be set to "CUSTOM_JAVA_OPTIONS" and I am trying to remove this from the original string "TESTSTR"
#echo off
set "TESTSTR=CUSTOM_JAVA_OPTIONS=-Dhttp.proxyHost=webcache.example.com
-Dhttp.proxyPort=8080"
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "tokens=1,2 delims==" %%G IN ("%TESTSTR%") DO (
echo Name=%%G
echo Value=%%H
set removestr=%%G
echo TESTSTR=!TESTSTR!
echo removestr=!removestr!
set "str=!TESTSTR:%removestr%=!"
echo str=!str!
)
pause
The above does not seem to work, it produces:
Name=CUSTOM_JAVA_OPTIONS
Value=-Dhttp.proxyHost
TESTSTR=CUSTOM_JAVA_OPTIONS=-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080
removestr=CUSTOM_JAVA_OPTIONS
str=TESTSTR:=
Expected result needs to be:
str=-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080
This could be simplified to:
#echo off
set "TESTSTR=CUSTOM_JAVA_OPTIONS=-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080"
FOR /F "tokens=1* delims==" %%G IN ("%TESTSTR%") DO set "str=%%H"
echo TESTSTR=%TESTSTR%
echo.str=%str%
pause
There are 2 tokens:
1. Text up to 1st delimiter
2. Everything else after first delimiter (*)
Note that by echoing the variables outside of the FOR loop you don't need to enable delayed expansion.
Your code fails entirely because %removestr% is expanded when the command is initially parsed, and your entire loop (code block) is parsed all at once. So %removestr% expands to the value that existed before your loop was entered. In your case, the variable is undefined. So !TESTSTR:%removestr%=! becomes !TESTSTR:=!, which finally becomes TESTSTR:=.
You get closer if you use %%G directly, instead of assigning an environment variable.
set str=!TESTSTR:%%G=! yields =-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080
You can then use set str=!str:~1!" to remove the leading =.
set str=!TESTSTR:%%G==! will not work because the search strings stops at the first occurrence of =, so the result is ==-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080
The RGuggisberg answer is the most convenient method to get your desired result. (You may want both %%G and %%H).
However, it technically does not break at the first =. It actually breaks at the first string of contiguous = because FOR /F does not parse empty tokens.
So for /f "tokens=1* delims==" %%G in ("A==B==C") yields A for %%G (correct), and B==C (incorrect) for %%H. The correct value should be =B==C.
If the first character after the = character is always a -, then the following method may also work for you:
#Echo Off
Set "TESTSTR=CUSTOM_JAVA_OPTIONS=-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080"
Set "REST=-%TESTSTR:*-=%"
Set "FIRST=%TESTSTR:-="&:"%"
Set "FIRST=%FIRST:~,-1%"
Echo [%FIRST%] [%REST%] & Pause
The bottom line is simply to show you the information.
This is the very first time i tried batch scripting so please bear with me.
I just wanted to read each line of my hosts file, and replace the line if it contains/matches a substring. I've seen a lot of answered questions about substrings here but I just can't make it work by using the provided solutions.
I have this code:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "hostspath=%SystemRoot%\System32\drivers\etc\hosts"
set "hostsbackuppath=c:\hosts"
>"%hostsbackuppath%.new" (
rem Parse the hosts file, skipping the already present hosts from our list.
rem Blank lines are preserved using findstr trick.
for /f "delims=: tokens=1*" %%a in ('%SystemRoot%\System32\findstr.exe /n /r /c:".*" "%hostspath%"') do (
set str1=%%b
if not x!str1:mydomainname=!==x!str1! (
rem Match found, replace this line.
echo "match!"
set matched=false
)
// Didn't match, do not replace
if not "!matched!"=="true" echo.%%b
)
)
I was trying out this solution to check for substring match among other else: Batch file: Find if substring is in string (not in a file)
Can someone help me? Thanks
SETLOCAL ENABLEDELAYEDEXPANSION
set "matched=true"
>"%hostsbackuppath%.new" (
for /f "delims=: tokens=1*" %%a in ('%SystemRoot%\System32\findstr.exe /n /r /c:".*" "%hostspath%"') do (
set "str1=%%b"
if not "!str1:mydomainname=!"=="!str1!" (
rem Match found, replace this line.
echo "match at %%b in line %%a"
set matched=false
)
// Didn't match, do not replace
if not "!matched!"=="true" echo.%%b
)
)
Hooley-dooley! Someone needs to learn to name variables appropriately.
First, you need to use setlocal enabledelayedexpansion - please see a thousand-and-one SO articles about delayed expansion.
Since str1 is varied within the loop, you need to use setlocal enabledelayedexpansion and !var! to access the varying value of var as %var% is the value at the time the for was encountered.
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. set /a can safely be used "quoteless".
FOr the same reason, quoting each side of a comparison is preferred as it makes a single token of a string containing separators like spaces.
Then you have a comment "match found" after which you set matched to false ?? Therefore you need to initialise match (to true)
Now quite what you want to do is obscure. On re-reading, you probably want to set "matched=true" as the first line within the loop, not outside as I have it, so that the value is re-set to true for each line found and then set to false if a match is found.
All this negative logic is insane. I need a strong cup of coffee.
I am trying to extract values for test_count, test_fail_count, test_pass_count from an XML file. This XML file has just one very long line:
<?xml version="1.0" encoding="UTF-8"?><ROOT test_count="22" test_fail_count="1" test_pass_count="21".......</ROOT>
Magoo helped me with the script, see his answer on my previous question
How to match strings from an XML file using batch and assign to variable?
This script worked initially. But when I incorporated this into my larger overall script, it failed. And I have not been able getting this script working again as expected since making this modification.
Any thoughts on how to debug this?
#ECHO Off
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\Report.xml"
SET "testcount="
SET "testfailcount="
echo forloop
FOR /f "usebackqdelims= " %%a IN ("%filename1%") DO (
SET "xmlline=%%a"
CALL :process
)
ECHO test count=%testcount% test fail count=%testfailcount%
GOTO :EOF
:process
echo in process
:: dispose of awkward characters
SET "xmlline=%xmlline:?= %"
SET "xmlline=%xmlline:>= %"
SET "xmlline=%xmlline:<= %"
CALL :select %xmlline%
GOTO :EOF
:select
echo in select
IF /i "%~1"=="" GOTO :EOF
IF DEFINED testcount IF DEFINED testfailcount GOTO :EOF
IF /i "%~1"=="test_count" SET /a testcount=%~2
IF /i "%~1"=="test_fail_count" SET /a testfailcount=%~2
SHIFT
GOTO select
GOTO :EOF
try the xpath.bat - it can extract values from xml files by an xpath expression and does not require installation of external tools:
call xpath.bat "report.xml" "//ROOT/#test_count"
call xpath.bat "report.xml" "//ROOT/#test_fail_count"
As in the metadata is pointed that file should be utf-8 you can check the encodings of the files on both machines.
The reason for not anymore working code is in the command line
FOR /f "usebackqdelims= " %%a IN ("%filename1%") DO (
There is a space character after the equal sign which results in splitting the line read from XML file up into multiple tokens using the space character as delimiter. So instead of getting entire XML file contents assigned to loop variable a, just the string up to first space character is assigned to the loop variable. For that reason the environment variable xmlline gets assigned just <?xml instead of the entire line read from XML file.
Change the line to
FOR /f "usebackq delims=" %%a IN ("%filename1%") DO (
There is no space after equal sign, but one between usebackq and delims=.
Or use the command line below as Magoo posted in his answer with no space after equal sign, but also no space between the two options usebackq and delims=.
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO (
Magoo explained in his comment also why the space character between the two options usebackq and delims= is not really necessary, but which I suggest to add for easier reading the options.
usebackq results in interpreting the file name enclosed in double quotes as file name and not as string to split up into tokens.
delims= with no characters specified after equal sign disables default splitting up of line read from file on spaces and horizontal tabs.
Open a command prompt window, run for /? and read the output help pages for help on for /F and its options.
i currently have this command for a batch file
for /F "skip=1 tokens=1 delims=\n" %i in (stats.txt) do echo %i
with the contents of stats.txt being
Title = Subaru's Great Rehab Strategy
URL = http://dynasty-scans.com/chapters/subarus_great_rehab_strategy
Tags = Subaru x Tsukasa[|]Yuri[|]
No. of Pages = 3
^ NOTE: the final line is actually blank
the idea of the line of code is to return the 2nd line with URL. the end goal would be that i would run this line in some sort of loop going though a series of ~12000+ stats.txt files and collecting all the URL lines into a single file
but when i run the command i get this
as you can see it has skipped the first line but it's cutting off where the n in dynasty and outputting the last 3 lines.
now if i remove delims=\n i get the same 3 lines but i don't get the first word before the space which seems to indicate that the value of delims is what splits a line into "tokens" which then i just grab the first one (and space must be the default)
when i go into notepad++, open the Find and Replace Dialog, turn Search Mode to extended and look for "\r\n" i get taken to the end of each line which is why i chose delims to be \n assuming this would then make the entire line one token
So my question is How can i get all of the 2nd line only of my stats.txt file?
The for /f loop already treats the carriage return and / or line feed as an end-of-line. No need to specify it as a delimiter. With delims=\n you're actually saying that all literal backslashes and letter n's should be treated as token delimiters. If you want the whole line, what you want is "skip=1 delims=".
Just out of habit, when reading the contents of a file with a for /f loop, I find it useful to enable usebackq just in case the filename / path contains a space or ampersand. That allows you to quote the filename to protect against such potential treachery.
#echo off
setlocal
for /F "usebackq skip=1 delims=" %%I in ("stats.txt") do if not defined URL set "URL=%%~I"
echo %URL%
Put into context, to use this to read many files named stats.txt and output the URLs into a single collection, enclose the whole thing in another for loop and enable delayed expansion.
#echo off
setlocal
>URLs.txt (
for /R %%N in ("*stats.txt") do (
for /F "usebackq skip=1 delims=" %%I in ("%%~fN") do (
if not defined URL set "URL=%%~I"
)
setlocal enabledelayedexpansion
echo(!URL!
endlocal
set "URL="
)
)
echo Done. The results are in URLs.txt.
If you want to strip the "URL = " from the beginning of each line and keep only the address, you could try changing your for /F parameters to "usebackq skip=1 tokens=3" if all the files follow the same format of URLSpace=Spacehttp://etc.. If you can't depend on that, or if any of the URLs might contain unencoded spaces, you could also change echo(!URL! to echo(!URL:*http=http!
You don't need to use a FOR /F loop, you can also read it with a SET /P
setlocal EnableDelayedExpansion
< stats.txt (
set /p line1=
set /p URL_Line=
)
echo(!URL_Line!
Try this from the command line:
(for /F "tokens=1* delims=:" %i in ('findstr "URL" stats*.txt') do echo %j) > output.txt
the idea ... is to return the 2nd line with URL
If you want to insert this line in a Batch file, just double the percent signs.
Try this from the prompt:
(for /f "tokens=1*delims=]" %a in ('find /v /n "" *.csv^|findstr /l /b "[2]"') do #echo %b)>u:\r1.txt
Where - I used *.csv for testing (substitute your own filemask) and I used u:\r1.txt for the result - substitute as seems fit (but don't output to a file tat fits your selected filemask !)
It works by prefixing each line in each file with a bracketed number [n] (find - /n=and number /v lines that do not match "" - an empty string); then selecting those lines that /l - literally /b at the beginning of the line match "[2]".
The result is all of the second-lines of the files, preceded by the literal "[2]". All we need to do then is tokenise the result, first token up to delimiter "]" will be "[2" assgned to %%a and remainder-of line (token *) will be assigned to %%b
Have you tried
for /F "skip=1 tokens=1 delims=\n" %i in (stats.txt) do echo %i && goto :eof
I haven't tested it as I don't have access to a Windows machine at the moment, but that should exit the for-loop after the first iteration, which is what you want.
I have bat-script with following code:
FOR /F "tokens=1,2 delims==" %%g in ("%CFGFILE%") do (
SET firstChar=%%g
SET firstChar=!firstChar:~1,1!
if /I "!firstChar!"=="#" (
echo %%g>>"%INSTALL_PATH%\tmp.cfg"
)else (
if /I "%%g"=="document.folder" (
SET path_written=TRUE
echo %%g=%DOC_FOLDER%>>"%INSTALL_PATH%\tmp.cfg"
)else (
rem next line is buggy
echo %%g=%%h>>"%INSTALL_PATH%\tmp.cfg"
)
)
)
The point is parsing cfg-file %CFGFILE% contents and copying every string without changes to new config-file, except only one string starting with "document.folder". This line must be changed. Problem is that the line after "next line is buggy" comment gives "c:\program files\myApp\original.cfg=" which is content of %CFGFILE% variable plus equals sign. Is this a bug or i've done something wrong? Is this connected with %%x variables visibility?
You have mis-identified the source of the problem! :-)
Your problem is in the very first line - your FOR statement is processing a string, not a file, because the IN() clause is enclosed by double quotes. If you want the IN() clause to be treated as a quoted file name, then you need to add USEBACKQ to your FOR options.
FOR /F "usebackq tokens=1,2 delims==" %%g in ("%CFGFILE%") do (
Just a heads up - even after the fix above, your code will not give the correct results if any of the following conditions appear
If any line contains ! then expansion of %%g or %%h will be corrupted because delayed expansion is enabled
Commented # line will be incomplete if the original contained =
Your normal lines will not be complete if there is a 2nd = in the original
My suggestion:
Use FART...
http://sourceforge.net/projects/fart-it/