Using Wildcard in Set command in batch file - batch-file

ok I am trying to strip the first two characters from a file I am using this script.
#echo off
Set "InputFile=C:\New Folder\test.txt"
Set "OutputFile=C:\New Folder\New\test.txt"
setLocal EnableDelayedExpansion > "%OutputFile%"
for /f "usebackq tokens=* delims= " %%a in ("%InputFile%") do (
set s=%%a
>> "%OutputFile%" echo.!s:~2!
)
which works perfect if I use the correct name. What I need to do is use a wild characters since the name of the file is different each time. When trying this it does not work.
#echo off
Set "InputFile=C:\New Folder\H*.txt"
Set "OutputFile=C:\New Folder\New\H*.txt"
setLocal EnableDelayedExpansion > "%OutputFile%"
for /f "usebackq tokens=* delims= " %%a in ("%InputFile%") do (
set s=%%a
>> "%OutputFile%" echo.!s:~2!
)

from
I'd like to use a wildcard with the SET command in Windows Batch so I don't have to know exactly what is in the string in order to match it
The asterisk IS a wildcard and WILL match multiple characters, but
will ONLY match everything from the very beginning of the string. Not
in the middle, and not from the end.
Useful Searches:
*x
*how are you? The above two searches CAN be matched. The first will match everything up to and including the first "x " it runs across.
The second one will match everything up to and including the first
"how are you?" it finds.
Legal, but Unuseful, searches:
x* Hello* OneThree The above three searches can NEVER be matched.
Oddly they are also legal, and will cause no errors. One exception:
Hello and x* WILL match themselves, but only if they are the very
beginning of the string. (Thanks Jeb!)

the surrounding for-loop does the wildcard processing (giving full qualified filenames)
#echo off
for /f %%i in ('dir /b C:\New Folder\H*.*') do (
echo processing %%i
Set "InputFile=C:\New Folder\%%i"
Set "OutputFile=C:\New Folder\New\%%i"
setLocal EnableDelayedExpansion > "%OutputFile%"
for /f "usebackq tokens=* delims= " %%a in ("%InputFile%") do (
set s=%%a
>> "%OutputFile%" echo.!s:~2!
)
endlocal
)
Note: if you have more than one line in those files, it will remove the first two characters from each line*.

Related

Reading large file from CMD and cutting information

I'm writing batch script on Windows, with it's help I would like to sort out information from many files to smaller files.
I got ~3000 long lines in log files, from whom I need get few things, basically there are name and value (example ",INC_LIMI=050,ISO_LIMI=050,INC_MLIM=074,"), and everything is separated with "," symbol. My question how you can read long string line and just read values like:
String LineString[]
LineString = Line.split(,)
String s = "INC_MLIM"
For elem in LineString
if elem.exist(s)
NewLine.append(elem)
and latter on just save to new file.
EDIT:
There is service.log file which contains multiple lines with same variable names, but I don't need all of them so the thing I'm trying to do is
From line :
",INC_MLIM=074,ISO_MLIM=074,LOC_LI_P=050,LOC_LI_L=050,TRI_LI_P=074,TRI_LI_L=074,"
Transform to new line structure with less variables and separate with tabs instead of comma. New line should look something like this:
"INC_MLIM=074 ISO_MLIM=074 LOC_LI_L=050 TRI_LI_L=074"
You don't state which values you want. I'll arbitrarily assume you want INC_LIMI and INC_MLIM.
Like most any text file manipulation, this is a pain to do with pure batch. But it is possible.
I'm assuming your lines are all <8192 characters long. If you have lines that are longer than that, then a pure batch solution is not possible, and you should skip right down to the bottom of this answer for a JREPL solution
Batch does not have a convenient split function that allows splitting at a specific user defined character. The FOR command almost works, but it also splits at ;, =, <tab>, and <space>. So it is not a good choice.
With the correct arcane syntax, you can use variable expansion find/replace to substitute a newline (0x0A) for every comma. This will generate one name=value pair per line, which is very convenient for letting FINDSTR filter out the values that you want.
Here is a solution that relies on a temporary table. This iterates all *.log files, and for each one, it creates output in *.log.new.
#echo off
setlocal enableDelayedExpansion
(set LF=^
%= This creates a newline 0x0A character =%
)
for %%N in ("!LF!") do for %%F in (*.log) do (
(
for /f "usebackq delims=" %%A in ("%%F") do (
set "ln=%%A"
echo(!ln:,=%%~N!
)
)>"%%F.temp"
findstr /b "INC_LIMI= INC_MLIM=" "%%F.temp" >"%%F.new"
del "%%F.temp"
)
type *.log.new
exit /b
Note that the above can fail if your log files contain !. This could be solved by toggling delayed expansion on and off as needed.
Some people don't like to use temp files. In this case, getting rid of the temp file introduces even more arcane batch constructs. But it does eliminate the ! delayed expansion issue, and the code is shorter. This version can also be significantly slower if the source files are very large.
#echo off
setlocal disableDelayedExpansion
(set LF=^
%= This creates a newline 0x0A character =%
)
for %%F in (*.log) do (
for /f "usebackq delims=" %%A in ("%%F") do (
set "ln=%%A"
cmd /v:on /c "for %%N in ("!LF!") do #echo(!ln:,=%%~N!"|findstr /b "INC_LIMI= INC_MLIM="
)
) >"%%F.new"
type *.log.new
exit /b
It is also possible to solve this without using FINDSTR. But this solution assumes the same name never appears more than once on any given line, and all found names have a value:
#echo off
setlocal disableDelayedExpansion
for %%F in (*.log) do (
for /f "usebackq delims=" %%A in ("%%F") do (
set "ln=,%%A"
for %%N in (INC_LIMI INC_MLIM) do call :findName %%N
)
) >"%%F.new"
type *.log.new
exit /b
:findName Name
setlocal enableDelayedExpansion
set "test=!ln!"
:loop
set "test2=!test:*,%1=!"
if "!test2!" equ "!test!" return
if not defined test2 return
if "!test2:~0,1!" neq "=" set "test=,!test2:*,=!" & goto :loop
for /f "delims=," %%V in ("!test2:~1!") do (
endlocal
echo(%1=%%V
)
exit /b
Here is a variation that handles empty values, but can break if a value contains quotes or poison characters:
#echo off
setlocal disableDelayedExpansion
for %%F in (*.log) do (
for /f "usebackq delims=" %%A in ("%%F") do (
set "ln=,%%A"
for %%N in (INC_LIMI INC_MLIM) do call :findName %%N
)
) >"%%F.new"
type *.log.new
exit /b
:findName Name
setlocal enableDelayedExpansion
set "test=!ln!"
:loop
set "test2=!test:*,%1=!"
if "!test2!" equ "!test!" return
if not defined test2 return
if "!test2:~0,1!" neq "=" set "test=,!test2:*,=!" & goto :loop
set "test2=%1!test2!
endlocal&echo(%test2:,=&rem %
exit /b
But I wouldn't use any of the above. In fact, I would never restrict myself to pure batch because text file manipulation is so darn inefficient and inscrutable.
Instead, I would use JREPL.BAT - a regular expression command line text processing utility. JREPL.BAT is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward - no 3rd party exe file required.
With JREPL, the solution is as simple as
#echo off
for %%F in (*.log) do call jrepl "(?:^|,)((?:INC_LIMI|INC_MLIM)=[^,]*)" "$txt=$1" /jmatchq /f "%%F" /o "%%F.new"
type *.log.new
Not only is the code nice and clean, it is much faster than any pure batch solution.

How to seprate space and more than one space delimiter in a batch For loop?

I have a problem to parse a text as a result of "net user" command
ABC ABCDE ABCDEFG
ABDCS HFJ ATi CObdnsen
to single users in a separate lines in batch script.
my code is this :
FOR /F "tokens=1,2,3 delims= " %%A in (test.txt) do echo %%A & echo %%B & echo %%C
the problem is the second line of my text that contains a username with space in the middle.
Note that i want this result :
ABC
ABCDE
ABCDEFG
ABDCS HFJ
ATi
CObdnsen
so what is the solution?
That looks to be fixed width columns, 25 characters per column. You could load each line into a variable and use substring operations to get the values.
#echo off
setlocal enableDelayedExpansion
for /f delims^=^ eol^= %%A in (test.txt) do (
set "ln=%%A"
echo col1=[!ln:~0,25!]
echo col2=[!ln:~25,25!]
echo col3=[!ln:~50!]
echo(
)
But that leaves you with the problem of removing trailing spaces from the end of each value. It is doable, but not easy with batch. My output from the above script encloses the values within square brackets so you can easily see the trailing space issue.
Instead of messing with the issue of removing trailing spaces, I would use my REPL.BAT utility to transform the data from fixed width to delimited format. REPL.BAT is a hybrid JScript/batch utility that performs a regular expression search/replace operation on stdin and writes the result to stdout. It is pure script that will run on any modern Windows machine from XP onward without needing any 3rd party executables. Full documentation is embedded within the script.
I would use REPL.BAT to insert a delimiter after the 25th and 50th characters. I would then use another REPL.BAT to strip out any spaces that precede the delimiter, and then a normal FOR /F can safely parse the values. I chose to use a pipe (|) as the delimiter.
#echo off
for /f "eol=| delims=| tokens=1-3" %%A in (
'type test.txt ^| repl "^(.{25})(.{25})" "$1|$2|" ^| repl " *\|" "|"'
) do (
echo col1=[%%A]
echo col2=[%%B]
echo col3=[%%C]
echo(
)
If you know that no value contains consecutive spaces, and all values are separated by at least 2 spaces, then you can get by with a single REPL that replaces 2 or more spaces with a delimiter
#echo off
for /f "eol=| delims=| tokens=1-3" %%A in (
'type test.txt ^| repl " {2,}" "|"'
) do (
echo col1=[%%A]
echo col2=[%%B]
echo col3=[%%C]
echo(
)
#echo off
setlocal enableextensions
for /f "tokens=*" %%a in ('net user^|find " "') do (
set "u=%%a"
setlocal enabledelayedexpansion
set "u=!u: =/!"
set "u=!u:/ =/!"
for /f "tokens=1 delims=/" %%b in ("!u!") do echo %%b
endlocal
)
endlocal
Not bullet proof. Will fail for user names with two spaces in it or (probably, not tested) with names of 24 characters or more (just to name two).
Thanks MC ND for your great idea... But your Answer should be edited like below for the best result :
#echo off
setlocal enableextensions
for /f "tokens=*" %%a in ('net user^|find " "') do (
set "u=%%a"
setlocal enabledelayedexpansion
set "u=!u: =/!"
set "u=!u:/ =/!"
for /f "tokens=1,2,3 delims=/" %%b in ("!u!") do echo %%b & echo %%c & echo %%d
endlocal
)
endlocal
Note that the delimiter is "5 spaces". because the most allowed user length is 20 character in windows. and the distance between the columns are 25 character.. so we have at least 5 spaces as a good delimiter. and we are not worry about spaces in the middle of a username.
and at last your should parse three tokens . Not only 1 token.
Thanks for your attention guys.

Searching for semicolon inside a string in batch causes weird issues

I am trying to write a simple batch command that reads the second line of a file (ProfileName), looks for a ";" in that line and then splits the line and stores it in two variables (ProfileName and RRSProfileName)
for /F "tokens=1 skip=1" %%A in (%ContentFilePath%) do ( SET ProfileName=%%A )
ECHO %ProfileName% | findstr /spin ";">nul
IF %ERRORLEVEL%==0 (
FOR /F "tokens=1,2 delims=;" %%A in ("%ProfileName%") do ( SET ProfileName=%%A& SET RRSProfileName=%%B )
)
ContentFile contains 2 lines
blah
blah1
For some reason, ProfileName variable does not get set if the "IF" statement is present. If I remove the "IF" statement, ProfileName gets set to blah1. This is bizzare. Can someone help? Both ProfileName and RRSProfileName are set to "" initially.
for /F "tokens=1,2 skip=1delims=;" %%A in (q22602116.txt) do SET "ProfileName=%%A"&SET "RRSProfileName=%%B"
worked perfectly well for me, assuming that Profilename should be set to the portion before the semicolon on the last line and RRSprofilename to the portion after (if it exists)
But - be careful of the presence of Spaces in a string-assignment - they are significant (original batch appeared to have trailing spaces...) - the quotes positioned just so should overcome this characteristic...
findstr /spin
it search in subdirectories (/s) omitting non printable files (/p) ignoring case (/i) numbering output lines (/n), and you are not giving any file, so findstr fails as its arguments are not correct. From here you get errorlevel 1, so, the code inside the if is not executed
Better use
for /F "usebackq tokens=1 skip=1" %%A in ("%ContentFilePath%") do ( SET "ProfileName=%%A" )
ECHO %ProfileName%| find ";" > nul
IF %ERRORLEVEL%==0 (
FOR /F "tokens=1,2 delims=;" %%A in ("%ProfileName%") do ( SET "ProfileName=%%A" & SET "RRSProfileName=%%B" )
)
Or just (if it is possible, i don't know all your cases), join the two steps into one
for /f "usebackq tokens=1,2 skip=1 delims=;" %%a in ("%ContentFilePath%"
) do ( set "ProfileName=%%a" & set "RRSProfileName=%%b" )
It reads the same required line, if semicolon is present, it is used to split the line and each of the variables retrieve the required content.

How to randomly rearrange lines in a text file using a batch file

I am creating a code that strips through different MAC addresses randomly, but cannot figure out how to do this. My thought on how to approach this is to randomize or rearrange the order of the MAC address in the text file with this script, but I cannot figure out how to do this with a batch file. How this will work is that it will read "maclist.txt", then create a new temp file with the random order "maclist_temp.txt", that will be the rearranged file. Then, it will pull this randomized file in order.
I have tried Google and searching the web, but I haven't found anything too useful. I'm still actively looking, but any advice would be extremely useful.
Something as simple as extracting and deleting a random line and then adding to the bottom might work. Randomization would be better though, but I want to keep the original list. Something like:
Make a temp copy of maclist.txt called maclist_temp.txt
Take one random MAC address, remove it from maclist_temp.txt
Readd it to the bottom
That is all I want, but any suggestions are welcome.
You may try this batch file to help you to shuffle your maclist.txt. The usage of the batch code is
C:\> type list.txt | shuffle.bat > maclist_temp.txt
Here are the contents of shuffle.bat:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET TmpFile=tmp%RANDOM%%RANDOM%.tmp
TYPE NUL >%Tmpfile%
FOR /F "tokens=*" %%i IN ('MORE') DO SET Key=!RANDOM!!RANDOM!!RANDOM!000000000000& ECHO !Key:~0,15!%%i>> %TmpFile%
FOR /F "tokens=*" %%i IN ('TYPE %TmpFile% ^| SORT') DO SET Line=%%i&ECHO.!Line:~15!
::DEL %TmpFile%
ENDLOCAL
After issuing the above command, maclist_temp.txt will contain a randomized list of MAC addresses.
Hope this helps.
Here is a simpler method to randomize/randomise a file, no temp files needed. You can even reuse the same input filename.
Limitations are: blank lines and line starting with ; will be skipped, and lines starting with = will have all leading = signs stripped and ^ characters are doubled.
#echo off
setlocal
for /f "delims=" %%a in (maclist.txt) do call set "$$%%random%%=%%a"
(for /f "tokens=1,* delims==" %%a in ('set $$') do echo(%%b)>newmaclist.txt
endlocal
I really like foxidrive's approach. Nevertheless I want to provide a solution with all the listed limitations eliminated (although cmd-related restrictions like file sizes < 2 GiB and line lengths < ~ 8 KiB remain).
The key is delayed expansion which needs to be toggled to not lose explamation marks. This solves all the potential problems with special characters like ^, &, %, !, (, ), <, >, | and ".
The counter index has been implemented in order not to lose a single line of the original text file, which could happen without, because random may return duplicate values; with index appended, the resulting variable names $$!random!.!index! are unique.
The findstr /N /R "^" command precedes every line of the original file with a line number followed by a colon. So no line appears empty to the for /F loop which would ignore such. The line number also implicitly solves the issue with leading semicolons, the default eol character of for /F.
Finally, everything up to and including the first colon (remember the said prefix added by findstr) is removed from every line before being output, hence no more leading equal-to signs are dismissed.
So here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set /A "index=0"
for /f "delims=" %%a in ('findstr /N /R "^" "%~dpn0.lst"') do (
setlocal EnableDelayedExpansion
for /F %%b in ("$$!random!.!index!") do (
endlocal
set "%%b=%%a"
)
set /A "index+=1"
)
> "%~dpn0.new" (
for /f "delims=" %%a in ('set $$') do (
set "item=%%a"
setlocal EnableDelayedExpansion
echo(!item:*:=!
endlocal
)
)
endlocal
exit /B
This seems to work. Feed it a command line parameter of the file to randomize.
#echo off
setlocal EnableDelayedExpansion
rem read the number of lines in the file
rem the find prepends the line number so we catch blank lines
for /f "delims=" %%n in ('find /c /v "" %1') do set "len=%%n"
set len=!len:*: =!
rem echo %1 has %len% lines
rem Relocate as many lines as there are lines in the file
for /l %%j in (1 1 !len!) do (
rem echo starting round %%j
rem geta random number between 1 and the number of lines in the file
set /a var=!random! %% !len! + 1
rem echo relocating line !var!
rem make sure there is no temp file
if exist %1.temp del %1.temp
rem read each line of the file, write any that don't match and then write the one that does
<%1 (
for /l %%i in (1 1 !len!) do (
rem if it is the target line then save it
if %%i == !var! (
set /p found=
rem echo saving !found!
)
rem if it is the target line then write it
if not %%i == !var! (
set /p other=
rem echo writing !other!
echo !other!>> %1.temp
)
)
rem now write the target line at the end
rem echo appending !found!
echo !found!>> %1.temp
)
rem replace the original with the temp version
move %1.temp %1>nul
)
rem print the result
type %1
Place in cmd file
for /f "tokens=2 delims=/" %%m in ('cmd /e:on /v:on /c "for /f %%f in (maclist.txt) do #echo !random!/%%f" ^| sort') do echo %%m
It spawns a cmd which reads the mac list in the inner for, prefixes a random value and a slash to the mac and sorts the list. Then this list is splitted in the outter for using the slash as delimiter and printing the mac address.

Batch - Combining Two Pieces of Text on One Line in a Loop

I'm trying to work up a batch file to combine two pieces of text on one line. The first is the filename; the second is the first line of text beginning with "To: ". I have been struggling for hours and this is as far as I've gotten:
#echo off
IF EXIST fullnames.txt DEL fullnames.txt
FOR /F %%g IN ('dir /b *.eml') DO (
SET filename=%~f1
SET toline=FINDSTR /B /C "To: "
ECHO %FILENAME%%TOLINE% >> fullnames.txt
)
and it doesn't work. I am getting errors or incorrect results almost regardless of what I put down for the filename line; haven't even begun to test the toline part. Any suggestions?
You already used FOR /F to capture the output of the DIR command. Capturing the output of FINDSTR is no different.
However, it is more efficient to use a simple FOR in place of the FOR /F with the DIR command.
You used %~f1 when I think you intended %%~fg.
You cannot expand a variable set within parentheses using %var%, you must use !var! delayed expansion instead. Type SET /? from the command line for more information - read the section starting with "Finally, support for delayed environment variable expansion has been
added..."
However, in your case, you can easily avoid using delayed expansion (not that it is a problem).
Instead of deleting any existing "fullnames.txt" and then appending output to it, it is more efficient to enclose the entire construct within parentheses and redirect all output to the file using the over-write mode.
#echo off
(
for %%F in (*.eml) do (
for "delims=" %%A in ('findstr /b /c:"To: " "%%F"') do echo %%F %%A
)
) >fullnames.txt
But the above solution, simple as it is, is much more complicated than it needs to be.
FINDSTR can process multiple files specified with wildcards, and it will prefix each matching line with the filename followed by a colon.
You can get your results simply from the command line without even using a batch file (or you could put this in a batch file):
findstr /b /c:"To: " *.eml >fullnames.txt
Edit
If you are concerned that a file might contain multiple lines starting with "To: ", and you only want to use the first line, then it is back to using a batch file:
#echo off
setlocal enableDelayedExpansion
set "prevFile="
(
for /f "tokens=1* delims=:" %%A in ('findstr /b /c:"To: " *.eml') do (
if "%%A" neq "!prevFile!" echo %%A: %%B
set "prevFile=%%A"
)
) >fullnames.txt
The above solution could fail if a filename contains !. Also, a path could be used with *.eml as long as the path does not contain a drive letter. Both the drive and ! issues can be resolved with additional modifications.
#echo off
setlocal EnableDelayedExpansion
if exist fullnames.txt del fullnames.txt
for %%f in (*.eml) do (
set toline=
for /F "delims=" %%l in ('findstr /B /C:"To: " "%%f"') do (
if not defined toline set "toline=%%l"
)
echo %%f!toline! >> fullnames.txt
)
EDIT: Simpler method added
The set toline= command delete 'toline' variable before each file is processed, so just the first "To: " matching line found is assigned to it and later shown using Delayed Expansion. However, this process may be achieved in a simpler way that doesn't require Delayed Expansion, as dbenham suggested:
#echo off
if exist fullnames.txt del fullnames.txt
for %%f in (*.eml) do (
set firstFind=
for /F "delims=" %%l in ('findstr /B /C:"To: " "%%f"') do (
if not defined firstFind set firstFind=now & echo %%f%%l >> fullnames.txt
)
)
You can't assign and use environment variables inside a for loop. Use delayed variable expansion or call a subroutine.
Delayed would look something like this
setlocal EnableDelayedExpansion
#echo off
IF EXIST fullnames.txt DEL fullnames.txt
FOR /F %%g IN ('dir /b *.eml') DO (
SET filename=%~f1
SET toline=FINDSTR /B /C "To: "
ECHO !FILENAME!!TOLINE! >> fullnames.txt
)
However, that doesn't look like it would work correctly anyway. I would do it like this
FOR /F %%g IN ('dir /b *.eml') DO call :process %%g
goto :eof
:process
SET filename=%~f1
SET toline=FINDSTR /B /C "To: "
ECHO %FILENAME%%TOLINE% >> fullnames.txt

Resources