I have a homework task which needed to be done using just batch script. I need to rewrite all the numbers in .txt file if they have dividing symbols . or , but those strings may contain both words and numbers. Also the result should stay in the same file.
For example:
Lorem ipsum dolor 12.3254
2556,4646 ex commodo
would become
Lorem ipsum dolor 123254
25564646 ex commodo
I started with some code that looks like this:
#echo off
setlocal EnableDelayedExpansion
SET verfile=%1
FOR /f "tokens=* delims= " %%A IN (%verfile%) DO (
SET "Var=%%A"
FOR /l %%i IN (0, 1, 9) DO (
echo !Var! | findstr "."
IF %ERRORLEVEL% EQU 0 (
)
)
And now I have no idea how to continue it.
Can you help me please?
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following settings for the source directory and filename are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "filename1=%sourcedir%\q73553463.txt"
:: remove variables starting #
FOR /F "delims==" %%b In ('set # 2^>Nul') DO SET "%%b="
SET /a linecount=0
:: Read entire file into memory
FOR /f "usebackqdelims=" %%e IN ("%filename1%") DO (
rem next line number
SET /a linecount +=1
rem record in memory
SET "#!linecount!=%%e"
)
:: process each line removing [,.] following a digit
:: and report to original file
(
FOR /L %%e IN (1,1,%linecount%) DO (
FOR %%c IN ("." ",") DO FOR /L %%y IN (0,1,9) DO SET "#%%e=!#%%e:%%y%%~c=%%y!"
ECHO !#%%e!
)
)>"%filename1%"
TYPE "%filename1%"
GOTO :EOF
The set # command will generate a list like
#whatever=something
#whateverelse=somethingelse
for all variables that are currently set and start #.BUT it would be unusual to have any variable set that starts # so set would generate an error. The 2^>nul sends any error-report (on standard device stderr, which is device 2) to nul that is, nowhere. The caret ^ is required because cmd needs to distinguish between a part of the set command and a part of the for.
The for/f...%%b using delims of = generates "tokens" from the list generated by the set. Tokens are separated by any sequence of any of the delimiters specified between = and ", and by default, "token1" is selected, so the strings applied to %%b are
#whatever
#whateverelse
and these variables need to be set to nothing.
See for /? from the prompt for documentation on for or browse thousands of examples on SO.
Use set "var=value" for setting string values - this avoids problems caused by trailing spaces which can cause chaos. Once bitten, twice shy.
Then we read the file. Using "delims=" sets no delimiters, hence the whole line forms "token 1" which is assigned to %%e. The usebackq changes the meaning of " so that a double-quoted filename may be used. The filename I've used includes a Space but if there are no separator characters in the name, the quotes and usebackq can be omitted (again, ref for/?)
Then add 1 to the linecount and record the line in memory by assigning it to #!linecount!. The !linecount! is required because linecount is varying within the block (parenthesised sequence of lines) - and with delayedexpansion enabled, %linecount% yields the value when the block was encountered, and !linecount! the run-time or instantaneous value - as it changes within the block.
Stephan's DELAYEDEXPANSION link
So - having established #1, #2..#%linecount% with the lines from the file, we can process those variables and produce a replacement file.
Note that there is a block formed by ( for...%%e...)>filename. This allows any echoed data within the block to be redirected to the file. > redirects to a new version whereas >> would append to any existing file.
So - we iterate through all of the #linenumber lines using a for /L on %%e. For each of these, we set %%c to "." and %%y to 0 to 9 and then replace any string %%y%%c with %%y (3. gets replaced by 3 for example). Then repeat with %%c set to ",". set /? provides documentation and browsing SO examples.
But Woah, Neddie! There's a little trick here. , is a list-separator so (. ,) won't work - it will be treated as (.). Using the quotes allows cmd to read each character separately, and we need to use %%~c instead of %%c to dump the quotes.
So - take a look around. You can do a lot with batch if you're devious enough. And no doubt you'll be challenged if you present this solution. Be ready to explain it. A really good way to follow what's happening is to turn echo on and watch the magic step-by-step. Use pause liberally and perhaps remove the >"%filename1%" to prevent the report going to the file while you're observing what's happening.
#echo off
setlocal enabledelayedexpansion
set "verfile=%~1"
echo before:
type "%verfile%"
for /f "usebackq tokens=*" %%a in ("%verfile%") do (
set strline=%%a
set strline=!strline:.=!
set strline=!strline:,=!
echo !strline!>>"%verfile%.tmp"
)
echo.
echo after:
type "%verfile%.tmp"
del /f /q "%verfile%.tmp"
Related
Alright, so I'm trying to read all lines from a text file. My current way is:
FOR /F "delims=0123456789 tokens=1,*" %%F IN ('find /v /n "" ^< myFile.bat') DO (
SET line = %%G
:: ^ Syntax errors at this line
SET line=!line:~1!
:: Yes, I have delayed expansions enabled due to a lot of fors and ifs needed
)
Basically the input file is another batch file which also contains the exact same code as above and other code with <, >, ^ etc. Once I read a line, it's basically impossible to use %%G as it will expand to stuff like:
SET line=ECHO Hello >> someFile
or
SET line=FOR /L %%G IN (1,1,5) ( SET "line=ECHO Hello %%G" & call :something & >nul SET /P =. )
Which will obviously not work. I've tried many workarounds (all have failed), including:
SET line="%%G
Which (most of the time) works, but from there using is with basically anything is near-impossible, even with something like:
:fixLine
SET line=%line:^=^^^^%
SET line=%line:<=^^^<%
SET line=%line:>=^^^>%
SET line=%line:'=^^^'%
SET line=%line:~2%
GOTO :returnFixLine
But all methods fail in some case or another. How can I read a file containing a batch script from another batch script, including special characters?
EDIT:
Doing
SET "line=%%G"
won't work, as %%G can contain quotes, and even if it doesn't, carets are still special in quotes:
SET "line=ECHO ^<Hello^>"
will turn into
SET "line=ECHO <Hello>"
Also, lines containing exclamation marks will get expanded too.
The first problems are the spaces in set line = %%G, as you set the variable line<space> instead of line.
And you prefix to the content a space.
You should use set line=%%G instead, but even that produces sometimes problems, when spaces are behind the %%G they are appended.
The best way is to use the extended SET syntax set "line=%%G".
Btw. There exists only one special charcter which can fail with a FOR-parameter expansion, that is the exclamation mark when delayed expansion is enabled.
The solution is to toggle delayed expansion.
setlocal DisableDelayedExpansion
FOR /F "delims= tokens=*" %%F IN ('find /v /n "" ^< myFile.bat') DO (
SET "line=%%F"
setlocal EnableDelayedExpansion
SET "line=!line:*]=!"
echo(Testoutput: !line!
endlocal
)
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 am trying to print Line 4, Col 21-50 out of a text file, can this be simply done under Windows somehow? I've been trying to do this:
FOR /F "usebackq tokens=1 delims=-" %G IN (%COMPUTERNAME%.txt) DO ECHO %G
This is just working out terribly. Can't I just print a specific set of lines?
I need this script to be run on multiple computers, ideally I'd like to convert it to a variable for use with slmgr -ipk, maybe someone has a better suggestion?
Contents of text file (I want the XXXXX-XXXXX-XXXXX-XXXXX-XXXXX portion):
==================================================
Product Name : Windows 7 Professional
Product ID : 00371-OEM-9044632-95844
Product Key : XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
Installation Folder : C:\Windows
Service Pack : Service Pack 1
Computer Name : LIBRA
Modified Time : 6/4/2015 7:26:54 PM
==================================================
if you want only the "Product Key" line you can try with
type %COMPUTERNAME%.txt|find /i "Product Key"
or
for /f "tokens=2 delims=:" %%# in (' type %COMPUTERNAME%.txt^|find /i "Product Key"') do echo %%#
For the task at hand, npocmaka's answer is the best suitable approach, as it does not insist on a fixed position of the string to extract from the file.
However, I want to provide a variant that sticks to a certain position.
The following code extracts the string placed at columns 21 to 50 in line 4 of file list.txt (the result is echoed (enclosed in "") and stored in variable LINE_TXT (without ""):
#echo off
for /F "tokens=1,* delims=:" %%L in (
'findstr /N /R ".*" "list.txt"'
) do (
if %%L equ 4 (
set "LINE_TXT=%%M"
goto :NEXT
)
)
:NEXT
if defined LINE_TXT set "LINE_TXT=%LINE_TXT:~20,29%"
echo."%LINE_TXT%"
The goto :NEXT command terminates the for /F loop at the given line; this is not mandatory but will improve performance for huge files (as long as the given line number is quite small).
To be more flexible, the following code can be used (define the string position in the initial set block):
#echo off
rem Define the string position here:
set FILE_TXT="list.txt"
set LINE_NUM=4
set COL_FROM=21
set COL_UPTO=50
setlocal EnableDelayedExpansion
set /A COL_UPTO-=COL_FROM
set /A COL_FROM-=1
for /F "tokens=1,* delims=:" %%L in (
'findstr /N /R ".*" %FILE_TXT%'
) do (
if %%L equ %LINE_NUM% (
set "LINE_TXT=%%M"
if defined LINE_TXT (
set "LINE_TXT=!LINE_TXT:~%COL_FROM%,%COL_UPTO%!"
)
goto :NEXT
)
)
:NEXT
endlocal & set "LINE_TXT=%LINE_TXT%"
echo."%LINE_TXT%"
Both of the above code snippets rely on the output of findstr /N /R ".*", which returns every line that matches the regular expression .*, meaning zero or more characters, which in turn is actually true for every line in the file; however, the switch /N defines to prefix each line with its line number, which I extract and compare with the originally defined one.
Here is another variant which uses for /F to directly loop through the content (lines) of the given text file, without using findstr:
#echo off
for /F "usebackq skip=3 eol== delims=" %%L in (
"list.txt"
) do (
set "LINE_TXT=%%L"
goto :NEXT
)
:NEXT
if defined LINE_TXT set "LINE_TXT=%LINE_TXT:~20,29%"
echo."%LINE_TXT%"
This method has got the better performance, because there is the skip option which skips parsing of and iterating through all lines (1 to 3) before the line of interest (4), opposed to the findstring variant.
However, there is one disadvantage:
for /F features an eol option which defines a character interpreted as line comment (and defaults to ;); there is no way to switch this option off as long as delims= defines no delimiters (last position in option string), which is mandatory here to return the line as is; so you have to find a character that does not appear as the first one in any line (I defined = here because your sample text file uses this as header/footer character only).
To extract a string from line 1, remove the skip option as skip=0 results in a syntax error.
Note that goto :NEXT is required here; otherwise, the last (non-empty) line of the file is extracted.
Although for /F does not iterate any empty lines in the file, this is no problem here as the skip option does not check the line content and skip over empty lines as well.
Finally, here is one more approach using more +3 where no text parsing is done. However, a temporary file is needed here to pass the text of the desired line to the variable LINE_TXT:
#echo off
set LINE_TXT=
more +3 "list.txt" > "list.tmp"
set /P LINE_TXT= < "list.tmp"
del /Q "list.tmp"
if defined LINE_TXT set "LINE_TXT=%LINE_TXT:~20,29%"
echo."%LINE_TXT%"
exit /B 0
This method avoids for /F and therefore the problem with the unwanted eol option as mentioned in the above solution. But this does not handle tabs correctly as more substitutes them with spaces (8 indent spaces as per default and configurable by the /Tn switch where n is the number of spaces).
I am very much a novice at Batch Scripting and i'm trying to write a simple script to READ from an INI file based on the Parameters that is passed when the batch file is called.
This is an example for what the INI file would look like:
[SETTING1]
Value1=Key1
Value2=Key2
Value3=Key3
[SETTING2]
Value1=Key1
Value2=Key2
Value3=Key3
[SETTING3]
Value1=Key1
Value2=Key2
Value3=Key3
I am running into a problem when it comes to reading ONLY the section that is called. It will read from any section that matches the "Value" and "Key" and i don't know how to limit it to only read the section with the settings.
The file is being called with this Parameter: run.bat run.ini setting2. My code below is what I have so far and I feel as if i have officially hit a wall. Can anyone help with this? Any help would greatly be appreciated.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET INIFile="%~f1"
for /f "usebackq delims=" %%a in (!INIFile!) do (
if %%a==[%2] (
SET yesMatch=%%a
for /f "tokens=1,2 delims==" %%a in (!yesMatch!) do (
if %%a==Value1 set Key1=%%b
if %%a==Value2 set Key2=%%b
if %%a==Value3 set Key3=%%b
)
ECHO !yesMatch!
ECHO !Key1!
ECHO !Key2!
ECHO !Key3!
pause
)
)
pause
exit /b
#ECHO OFF
SETLOCAL
SET INIFile="%~f1"
SET "FLAG="
for /f "usebackq tokens=1,*eol=|delims==" %%a in (%INIFile%) do (
IF "%%b"=="" (
REM No "=" so section
IF /i "%%a"=="[%2]" (SET flag=Y) ELSE (SET "flag=")
) ELSE IF defined flag (
REM data line - only if FLAG is defined
REM set values defined
SET "%%a=%%b"
REM pick particular values
if /i "%%a"=="Value1" set "Key1=%%b"
if /i "%%a"=="Value2" set "Key2=%%b"
if /i "%%a"=="Value3" set "Key3=%%b"
)
)
SET key
SET val
GOTO :EOF
Here's a way to get your values.
The data in the file is either [section] or name=value so settling delims to = will assign either section-only to %%a or name to %%a and value to %%b.
The flag is only set (defined) after its appropriate section is encountered, and cleared on the next section. Only of it is defined will the assignment take place.
The advantage of the simple set %%a=%%b is that it results in setting whatever values are defined in the section - no changes to the code need to take place if new values are added. Your original version has the advantage of picking particular values and setting only those. You pays your money, you takes your choice.
Note that the /i switch on an if statement makes the comparison case-insensitive.
Nota also the use of set "value=string" which ensures that trailing spaces on a line are not included in the value assigned.
edit : By default, the end of line character is ; so any line starting ; is ignored by for/f. The consequence is that the values for the ;-commented-out section would override the values set for the previous section.
Setting eol to | should cure the problem. It really doesn't matter what eol is set to; it's exactly one character which may not appear anywhere in the INI-file (else that line would appear truncated.)
It is possible, if necessary, to set eol to control-Z but selecting an unused character is easier...
Consequently, a one-line change - the eol parameter is included in the for /f options.
There are more than 10 html files with image tags. Every time we deploy our build onto test site we need to change the img source. for eg <img src=/live/Content/xyz.png />
to <img src=/test/Content/xyz.png />.
After looking around and reading for sometime, i have come up with the following batch script, however i cant figure out how do i go further from here :
for /r %%i in (*.html) do echo %%i
for %%f in (*.html) do (
FOR /F %%L IN (%%f) DO (
SET "line=%%L"
SETLOCAL ENABLEDELAYEDEXPANSION
SET "x= <--------------------WHAT DO I SET HERE?
echo %x%
ENDLOCAL )) pause
This is my first batch script, could anyone please guide me in the right direction?
#ECHO OFF
SETLOCAL enabledelayedexpansion
for /r U:\ %%i in (*.html) do (
echo found %%i
SET outfile="%%~dpni.lmth"
(
SETLOCAL disabledelayedexpansion
FOR /F "usebackq delims=" %%L IN ("%%i") DO (
SET "line=%%L"
SETLOCAL ENABLEDELAYEDEXPANSION
SET "line=!line:/live/=/test/!
echo !line!
ENDLOCAL
)
ENDLOCAL
)>!outfile!
)
pause
GOTO :EOF
How about this development?
Notes:
I've modified your FOR/R to ECHO the HTML file being processed and use %%i rather than switching to %%f. U: is my RAMDRIVE; you'd need to modify that to suit.
outfile is set to generate a filename which matches the HTML filename, but with a .lmth extension (can't update in-place) - it gets that from the ~dpn prefixing the i, which means the drive, path and name of the file %%i. It's quoted to take care of potential spaces in the filename or pathname.
The next logical statement is (for /f...[lines] )>!outfile! which sends any echoed text to a NEW file !outfile!. The enabledelayedexpansion in the second physical line of the batch makes !outfile! the RUN-TIME value - as it is changed within the FOR r outer loop.
Since the actual HTML filename in %%i may contain spaces, it needs to be quoted, hence the 'usebackq' clause in the FOR/F. The delims= clause ensures that the ENTIRE line from the file "%%i" is applied to %%L - not just the first token (well, actually, makes the entire line the first token).
The SET command substitutes the string "/test/" for any occurrence of "/live/" in the RUN-TIME value of the variable lineand assigns the result to line. The resultant value is then ECHOd - which is redirected to outfile
Note that in your original, you would be assigning x in the set x= but echo %x% would have reproduced x as it stood when the line was PARSED because batch substitutes the value of any variable for %var% as part of the parsing phase. Consequently, the line would have become simply ECHO (since x would likely be unassigned) and bizarrely would have reported the echo state (Echo is OFF)
A couple of gatchas here. First, % and some other characters are notoriously hard to process with batch, so be careful. Next, FOR/F will bypass empty lines. This can be overcome if required. Third, this will replace ANY occurrence of /live/ in any case with /test/
Good luck!
Edit to support exclamation marks: 20130711T0624Z
Added SETLOCAL enabledelayedexpansion line and ENDLOCAL just before )>!outfile! to match