I would like to replace the current month to another month while everything else stay the same.
I have hundred over files which I thought of using rename function in CMD.
My file before is apple Oct'18 banana.xlsx, I want change is it to 'apple Nov'18 banana.xlsx'
The closest I gotten was using Ren *oct*.xlsx "* Nov'18 banana*".xlsx
but the result I got in return was apple Oct Nov banana.xlsx
However, I cannot replace the Oct completely.
Try ren *.xlsx ??????Nov??????????.*
* is a wildcard to match every ".xlsx" extension.
? is a wildcard to make every character in the original file stay unchanged.
I think there is no way to accomplish your task by a single ren command line.
You could write a batch file with the following code and name it ren_date.bat:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=." & rem // (root directory; `.` is current, `%~dp0.` is parent)
set "_FROM=%~1" & rem // (name portion to be replaced; `%~1` is 1st argument)
set "_TO=%~2" & rem // (name portion to be inserted; `%~2` is 2st argument)
rem // Enumerate all matching files (note the SPACE behind the asterisk):
for /F "delims=| eol=|" %%J in ('dir /B /A:-D "%_ROOT%\* %_FROM%*.xlsx"') do (
rem // Store current file name, initialise variable for new name:
set "FILE=%%J" & set "NAME="
rem // Toggle delayed expansion to avoid loss of exclamation marks:
setlocal EnableDelayedExpansion
rem /* Replace each SPACE in current file name by `" "` and enclose the
rem whole string in between `""`, so we get a list of quoted items,
rem which reflect all the SPACE-separated file name parts: */
for %%I in ("!FILE: =" "!") do (
endlocal
rem // Store current file name part:
set "PART=%%~I"
setlocal EnableDelayedExpansion
rem /* Assemble new name, performing the intended substitution of the
rem predefined sub-strings; the `for /F` loop is needed to transport
rem the resulting new file name over the `endlocal` barrier: */
for /F "delims=| eol=|" %%K in ("!NAME! !PART:%_FROM%=%_TO%!") do (
endlocal
rem // Store the assembled name portion:
set "NAME=%%K"
setlocal EnableDelayedExpansion
)
)
rem // Now rename file to the assembled new name:
ren "!_ROOT!\!FILE!" "!NAME:~1!"
endlocal
)
endlocal
exit /B
Then run it in cmd using the following command lines, given that you are in the directory that contains the files to rename:
ren_date.bat "Oct'18" "Nov'18"
Referring to the example from my answer to a former question of yours, this should rename the files:
and some pears Oct'18_xyz.xlsx
apples Oct'18.xlsx
bananas Oct'18.xlsx
more fruit Oct'18_xyz.xlsx
oranges Oct'18.xlsx
plus peaches Oct'18_xyz.xlsx
strawberries Oct'18 abcdef.xlsx
To these ones:
and some pears Nov'18_xyz.xlsx
apples Nov'18.xlsx
bananas Nov'18.xlsx
more fruit Nov'18_xyz.xlsx
oranges Nov'18.xlsx
plus peaches Nov'18_xyz.xlsx
strawberries Nov'18 abcdef.xlsx
(To write the above code into a command line directly in cmd is not possible, I believe, particularly due to the toggling of delayed expansion to make the code safe.)
As batch only knows the variable type string ad isn't good at date time calculations,
I suggest to use PowerShell for this.
With the
[datetime] type and
the .AddMonths() method
the Regular expression based -match operator
it will do even crossing year boundaries:
## Q:\Test\2018\12\13\SO_53757192.ps1
$CI = [cultureinfo]::InvariantCulture
$dt = ' MMM\''yy '
Get-ChildItem '* ???''[0-9][0-9] *.xlsx' |
Where-Object BaseName -match ' (Jan|Feb|Mar|Apr|Jun|Jul|Aug|Sep|Oct|Nov|Dec)''(\d{2}) ' |
Rename-Item -NewName {$_.Name -replace $Matches[0],
[datetime]::ParseExact($matches[0],$dt,$CI).AddMonths(1).ToString($dt)} # -WhatIf
You can invoke it from batch as a file
powershell -NoP -Ex bypass -File ".\SO_53757192.ps1"
Or cram it into one line
powershell -NoP -C "$CI=[cultureinfo]::InvariantCulture;$dt=' MMM\''yy ';Get-ChildItem '* ???''[0-9][0-9] *.xlsx'|Where-Object BaseName -match ' (Jan|Feb|Mar|Apr|Jun|Jul|Aug|Sep|Oct|Nov|Dec)''(\d{2}) '|Rename-Item -NewName {$_.Name -replace $Matches[0],[datetime]::ParseExact($matches[0],$dt,$CI).AddMonths(1).ToString($dt)}"
Related
I need to create a cmd script (and somehow I did) that extracts some lines of text from a series of files and puts them in a new txt file.
The source files are like this:
%
!
! AAA
!
! ------------------------ SOME TEXT ABCDEFGHIJKLMN --------------------------
!
! BBB
! ----------------------------------------------------------------------------
! T5 PUNTA ø 6.5/9.5~ $ 63~
! ----------------------------------------------------------------------------
! T12 PUNTA ø 2.5~ $ 39~
! ----------------------------------------------------------------------------
!
! SOME OTHER TEXT
!
! 1] ABC
! 2] DEF
! 3] ...
OTHER LINE 1
OTHER LINE 2
ETC
%
And the lines I need to extract are the ones between two "! ----------------------------------------------------------------------------" so, in this case, T5 PUNTA ø 6.5/9.5~ $ 63~ and T12 PUNTA ø 2.5~ $ 39~.
I was trying some regular expressions with findstr to match a line with ! only after the relevant lines, which indicates the end of the search, until I came up (by pure chance) with an instruction that matches all and only the lines that I need (luck, I guess).
The snippet is this:
#echo off
setlocal enabledelayedexpansion
if exist output.txt ( break > output.txt )
for /r <path> %%g in (<filename>) do (
...
for /f "tokens=* delims= " %%a in (%%g) do (
echo %%a | findstr /r /c:^\!$ >nul
if errorlevel 1 (...)
) else ( echo %%a >> srcoutput.txt
...
)
)
)
Please focus on the instruction echo %%a | findstr /r /c:^\!$ >nul.
This, for a reason I don't know, matches only the lines T5 PUNTA ø 6.5/9.5~ $ 63~ and T12 PUNTA ø 2.5~ $ 39~. Which is exactly what I want, but I don't know why it works!
Can someone help me understand why this simple expression ^\!$ works?
In my (wrong) understanding, it should match only a line with a single ! (which I had escaped, because otherwise it didn't work) at the beginning and at the end.
Thank you in advance
Actually the comand line:
echo %%a | findstr /r /c:^\!$ >nul
just returns lines that contain a $-character.
This is what happens, step by step:
the command line becomes parsed to (assuming %%a holds <expanded text>):
echo <expanded text> | findstr /r /c:\!$ >nul
so the (unquoted) caret (^) disappears as it is the escape character for cmd; since \ has no special meaning, you could just omit the ^ after all;
since delayed expansion is enabled (actually unnecessarily), the !-sign disappears, because there is only one, so the command line becomes:
echo <expanded text> | findstr /r /c:\$ >nul
the \-symbol acts as an escape character (though particularly for findstr!), so the $-sign loses its special meaning in regular expression (/R) mode (namely to anchor a match to the end of a line) and is therefore treated as a literal character;
the left side of the pipe passes on the text <expanded text> (with a trailing SPACE since there is one before the |), and the right side eventually searches for literal $-characters in that text;
You would achieve the exactly same result using the following command line instead:
echo %%a | findstr /C:$ > nul
though I would rather write it as:
echo(%%a| findstr /C:"$" > nul
to avoid the trailing SPACE and to safely echo any text.
For this task I would probably go for another approach (see all the explanatory rem remarks):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=D:\Target\Path" & rem // (path to root directory)
set "_MASK=*.txt" & rem // (name or mask of files to process)
set "_SAVE=D:\Path\To\output.txt" & rem // (location of output file)
rem // Gather line-feed character:
(set ^"_LF=^
%= blank line =%
^")
rem // Gather carriage-return character:
for /F %%C in ('copy /Z "%~f0" nul') do set "_CR=%%C"
rem // Open output file only once and write to it:
> "%_SAVE%" (
rem // Find matching files and loop through them:
for /R "%_ROOT%" %%F in ("%_MASK%") do (
rem // Check for file existence (only necessary when a dedicated name is given):
if exist "%%~F" (
rem // Store path of current file:
set "FILE=%%~F"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Remove remaining quotes (only necessary when a dedicated name is given):
set "FILE=!FILE:"=!
rem /* Do a multi-line search by `findstr`, which only returns the first line;
rem the searched string is:
rem # anchored to the beginning of a line,
rem # an `!`, a space and a `T`, then
rem # some arbitrary text (without line-breaks), then
rem # a line-break, then another `!` and a space, then
rem # a sequence of one or more `-`,
rem # anchored to the end of a line;
rem only the portion before the explicit line-break is then returned: */
findstr /R /C:"^^^! T.*~!_CR!!_LF!^! --*$" "!FILE!"
endlocal
)
)
)
endlocal
exit /B
This does not exactly search for lines between ! --- etc., but it searches for two adjacent lines where the first one begins with ! + SPACE + T and ends with ~, and the second one consists of ! + SPACE + a sequence of one or more -.
If the input file contains Unix-/Linux-style line-breaks rather than DOS-/Windows-style ones, replace !_CR!!_LF! in the findstr search string in the script by !_LF!.
I have decided to post this as a potential method of achieving your intented goal. It uses a different methodology from the currently accepted answer, the idea is to retrieve the ! ----etc. line numbers, then determine if the lines between any two of them have the required content. This means that it isn't looking to match specific content between those lines and should therefore work, whichever characters your strings are formed using.
#Echo Off
SetLocal EnableExtensions
Set "InFile=somefile.ext"
Set "OutFile=someoutfile.ext"
Set "$#="&For /F "Delims=:" %%G In (
'"%__AppDir__%findstr.exe /RNC:"^! --*$" "%InFile%""')Do (
Set /A _2=%%G-2&Call Set "$#= %%G %%$#%%"&Call Set "$2= %%_2%% %%$2%%")
If Not Defined $# Echo No Matches&%__AppDir__%timeout.exe -3&Exit /B
SetLocal EnableDelayedExpansion
For %%G In (%$2%)Do If "!$#: %%G =!"=="%$#%" Set "$2=!$2: %%G =!"
For %%G In (%$2%)Do Set /A _1=%%G+1&Set "$1= !_1! !$1!"
EndLocal&(For %%G In (%$1%)Do For /F "Tokens=1*Delims=]" %%H In (
'%__AppDir__%find.exe /V /N "" "%InFile%"^
^|%__AppDir__%findstr.exe "^\[%%G\]"')Do Echo %%I)>"%OutFile%"
GoTo :EOF
Just change your input file and output file names on lines 3 and 4, as required.
Please note that I'm unble to test this, so it may not work, or could possibly work in the wrong way. Please test it on files with various similar formats, before using it for real!
I have to write a batch script that loops over files and replaces stuff. Here is a sample data from the file.
1068 1181408 META METADATA 20150618201505211
20400693 400693
30H13UC 23 00
4010 618114915
4020 3
4030 0455
4040 400
4050 0029
4070 ROck
4080 XX SMALL
4090 Worley Stone
Now I need to find the Number starting with 20 and replace the next digits frm 3rd position with 10101.
Eg: In the file the 1st number stating with 20 is the 2nd line after the line beginning with 1068.
20400693 -> 2010101
and also in 340th position in the same line.
in the same line the number in 340th positon is 400693
400693 -> 10101
This pattern may or may not occur multiple times in same file
Now I can loop over the files like
for /r %i in (*)
But how do I write out the replacement part.
#ECHO OFF
SETLOCAL
:: The directory to look for data files and to place processed files
SET "sourcedir=U:\sourcedir\t w o"
SET "destdir=U:\destdir"
:: the start of the line, and length-to-match
SET "replaceinlines=20"
SET /a lengthofmatch=2
:: Replacement text, length-to-replace, column-for secondary-replacement
SET "replaceby=10101"
SET /a replacelength=6
SET /a replacecolumn=332
:: Replace-only-if-match ?
SET "replaceifmatch=Y"
:: calculate length of second-segment-to-preserve and its start-position
SET /a seg2start=replacelength+lengthofmatch
SET /a seg2=replacecolumn-seg2start
SET /a seg3start=replacecolumn+replacelength
::
FOR /f "tokens=1*delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*" '
) DO (
FOR /f "usebackqdelims=" %%x IN ("%sourcedir%\%%a") DO SET "line=%%x"&call:process
) >"%destdir%\%%a"
GOTO :EOF
:process
:: does the start-of-line match?
CALL SET "startofline=%%line:~0,%lengthofmatch%%%"
IF "%startofline%" neq "%replaceinlines%" GOTO report
:: matched start-of-line; pick up data-to-replace
CALL SET "data1=%%line:~%lengthofmatch%,%replacelength%%%"
CALL SET "data2=%%line:~%replacecolumn%,%replacelength%%%"
::
:: Not sure about this - replace-both-regardless or replace-if-data-matches
::
IF "replaceifmatch"=="Y" IF "%data1%" neq "%data2%" GOTO report
CALL SET "line=%startofline%%replaceby%%%line:~%seg2start%,%seg2%%%%replaceby%%%line:~%seg3start%%%"
:report
ECHO(%line%
GOTO :eof
You would need to change the setting of sourcedir and destdir to suit your circumstances. Produces a new file with the same filename as the source in the destination directory. U: is my test drive.
Patching your supplied data yielded the target 400693 at column 332, ot 340 as claimed.
The pattern to match at the start of the lines is placed in replaceinlines and its length in lengthofmatch
The length of the text-to-be-replaced is 6 (replacelength) but you have a replacement string of length 5.?? (replaceby)
I look at the line as havng 4 segments - the first is the 20 and the following 6 characters, the second the space between that and the second 'to be replaced' string; the last (which I named seg3 but should be seg4 is the part which follows the second 'to-be-replaced' string.
You don't say whether the replacement is to take place only if the two 'to-be-replaced' strings match or regardless, so I supplied a switch replaceifmatch - Y means "if the two match, replace both". Setting replaceifmatch to something else will replace regardless.
Beyond that, it's a simple matter of calculating the column-positions and lengths from the data provided and using call set to apply the calculated values to the strings of interest.
You can use Windows Scripting Host to get what you want.
Create a file called say, "1.wcf", and copy/paste the following:
<job>
<script language="JavaScript">
var fso = new ActiveXObject("Scripting.FileSystemObject");
var files = new Enumerator(fso.getFolder(".").files);
var count = 0;
for (; !files.atEnd(); files.moveNext())
{
var file = ""+files.item(); // make it string
if (!file.match(/.*\.txt$/))
{ continue; WScript.echo("Found itself, skipping"); }
//WScript.echo("Replacing in " + file);
var f1 = fso.OpenTextFile(file, 1);
var text = f1.ReadAll();
f1.close();
var lines = text.split("\r\n");
for (var i = 0; i < lines.length; i++)
{
var m = lines[i].match(/^20(\d+)/);
if (m)
{
lines[i] = lines[i].replace(new RegExp(m[1], "g"), '10101');
//WScript.echo("Replaced in " + lines[i]);
}
}
var f2 = fso.OpenTextFile(file, 2);
f2.Write(lines.join("\r\n"));
f2.close();
}
WScript.echo("Replaced "+count+" files");
</script>
</job>
Then, copy this file into the folder with TXT files, and run. It will process each TXT, and if a line in the TXT file starts with 20, the rest of the adjoining digits are captured into Group 1, and then are used to replace all such digit sequences on that line.
Then, the file is re-written with the updated contents.
Your spec is a bit imprecise - position of 40 string is not as stated, and you don't state whether the spacing of the replacement line matters.
Since you tagged your question with regex, I think you will be interested in my JREPL.BAT regular expression text processing utility. It is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward.
This first solution simply replaces the digits following 20 and 40 with the new string, disregarding original string length. So the position of the 40 string may change (does change in your example).
#echo off
for /r %%F in (*) do call jrepl "^(1068 .*\n20)\d+( +40)\d+ *$" "$110101$210101" /m f "%%F" /o -
Here is a more complicated solution that preserves the position of the 40 string (position 332 in your example)
#echo off
for /r %%F in (*) do call jrepl "^(1068 .*\n20)(\d+ +)40\d+ *$" "$1+'10101'+Array($2.length-5+1).join(' ')+4010101" /m /j /f "%%F" /o -
This final solution assumes the line is formatted with fixed width, and both the 20 and 40 numbers have maximum length of 10. This solution preserves both the position of the numbers, and the total length of the line:
#echo off
for /r %%F in (*) do jrepl jrepl "^(1068 .*\n20)\d+ *( {322}40)\d+ *$" "$110101 $210101 " /m /f "%%F" /o -
The method below assume that there are not empty lines in the files. This point may be fixed, if needed.
#echo off
setlocal EnableDelayedExpansion
rem Set working values
set "find=20"
set "replace=10101"
rem Process all files in current folder and below it
for /F "delims=" %%a in ('dir /A-D /S /B *.*') do (
rem Read this file via redirected input
rem and create a .tmp extension copy of it via redirected output
< "%%a" (
rem Read the first line
set /P "line="
set lastLine=1
rem Find the number of the lines that start with "20"
for /F "tokens=1,2 delims=: " %%b in ('findstr /N "^%find%" "%%a"') do (
rem Copy the lines before this one
set /A lines=%%b-lastLine, lastLine=%%b
for /L %%i in (1,1,!lines!) do set /P "line=!line!" & echo/
rem Process this line as desired:
rem Get the first token in this line
set "token=%%c"
rem Get the pattern to replace removing "20" from beginning of the token
rem and replace it in the entire line
for /F %%d in ("!token:*%find%=!") do set "line=!line:%%d=%replace%!"
)
rem Copy the last replaced line
echo !line!
rem Copy the rest of lines after the last replaced one
findstr "^"
) > "%%~Na.tmp"
rem Replace the original file by the processed one
move /Y "%%~Na.tmp" "%%a" > NUL
)
I have a log file I need to process and extract data from. Each line contains a string of an event log output. Unfortunately, the parts of the string is NOT uniformly formatted. Here are a few example lines:
"Some random length string. 0x8dda46 0x1 0x384 C:\Program Files (x86)\some\path\foo0.exe "
"Some random leeeength string. 0xa95ac2 0x8cc C:\Program Files (x86)\some\path\foo1.exe %%1936 0xcc0 "
"Some random leength string. 0xbcd668 0x330 C:\Program Files (x86)\some\path\foo2.exe %%1936 0xf38 "
"Some random leeeeeeeength string. 0xbcd668 0x1 0x330 C:\Program Files (x86)\some\path\foo2.exe "
"Some random leeength string. 0x352c44 0xfc0 C:\Program Files (x86)\some\path\foo3.exe %%1936 0x92c "
"Some random leeeeength string. 0xa95ac2 0x0 0x8cc C:\Program Files (x86)\some\path\foo1.exe "
"Some random leength string. 0x352c44 0x0 0xfc0 C:\Program Files (x86)\some\path\foo3.exe "
I need to extract the "foo.exe" file name without the full path and the HEX value just before the "C:\Progra..." (it's the process ID)
so I want the output be:
0x384 foo0.exe
0x8cc foo1.exe
0x330 foo2.exe
0x330 foo2.exe
0xfc0 foo3.exe
0x8cc foo1.exe
0xfc0 foo3.exe
I'm trying to achieve the goal with as less "hard coded" search/replace as possible since many parts of the string is not going to be the same content or same length. I tried to use FOR /F to split the string, but I have no way to locate the two columns as they are always changing. Only thing is constant is the "C:\Program Files (x86)" part. (Plus FOR has a 52 variable limit)
I have written some tricky batch files, but I'm starting to think I'm asking too much of DOS ;-)
Thanks in advance for any help!
#ECHO OFF
SETLOCAL
FOR /f "tokens=1*delims=." %%a IN (q28333414.txt) DO (
FOR /f "tokens=1*delims=:" %%c IN ("%%~b") DO CALL :process %%c&CALL :report "%%d
)
GOTO :EOF
:process
SET hexval=%~3
IF DEFINED hexval shift&GOTO process
SET "hexval=%~1"
SET "drive=%~2:"
GOTO :eof
:report
SET "line=%drive%%~1"
SET "line="%line:.exe=.exe"%"
FOR %%r IN (%line%) DO ECHO %hexval% %%~nxr&GOTO :eof
I used a file named q28333414.txt containing your data for my testing.
The first process simply throws away each (space-delimited) parameter between the . and : until there are exactly two left - the required hexval and the drive letter.
The report process re-attaches the drive letter and encloses it and the .exe name in quotes. the for %%r picks the first string, shucks off the quotes, spits out the result and all's done.
Edit : fixed report to show name and extension of file only as required and dbenham comment
Breaking news: (literally!)
#ECHO OFF
SETLOCAL enabledelayedexpansion
FOR /f "delims=" %%a IN (q28333414.txt) DO SET "line=%%~a"&CALL :process "!line::=" "!"
)
GOTO :EOF
:process
SET "hexval=%~3"
IF DEFINED hexval shift&GOTO process
CALL :lastbar1 %%~1
SET "filename=%~2"
SET filename="c:%filename:.exe =.exe" %
FOR %%r IN (%filename%) DO ECHO %hexval% %%~nxr&GOTO :eof
GOTO :eof
:lastbar1
SET "hexval=%~3"
IF DEFINED hexval shift&GOTO lastbar1
SET "hexval=%~1"
GOTO :eof
OK - let's try this, then.
For each line, replace all evil colons with " " and pass resultant quoted-string sequence to a subroutine.
Shift the parameters until there are but 2, which will be the string before and after the final countdown - er, colon.
Repeat the process for the first parameter. The penultimate value is the required hexval.
with the second parameter, add "c: before and " after any .exe, so the result is a quoted full-filename and dross; spit out the hexval and filename and done...
small revision in the rather dim light of the "&" comment - the famous set "var=whatever" formula fails with & included in this case (as in subdirectory "Documents & Settings") so the enclosing quotes can be removed as trailing spaces are not relevant. Would have been useful to know what the test data triggering the problem was though - reduces guesswork.
Any good regex utility you can lay your hands on should be able to solve your problem. I like to use my JREPL.BAT hybrid JScript/batch utility. It is pure script that runs natively on any Windows machine from XP onward.
Assuming your file is test.log, then I would use:
jrepl ".* (0x[0-9A-F]+) C:\\Program Files \(x86\)\\(?:.*\\)?([^\\]+\.exe) .*" "$1 $2" /i /f test.log
On each line it looks for the last occurrence of a hex string sandwiched by spaces that precedes a file path that begins with "C:\Program Files (x86)\" and ends with ".exe". I made the search ignore case.
This solution assumes that there are not backslashes into the random string.
#echo off
setlocal EnableDelayedExpansion
for /F "tokens=1-5 delims=\" %%a in (logFile.txt) do (
rem Extract the HEX value
for %%A in (%%~a) do (
set "value=!lastButOne!"
set "lastButOne=%%A"
)
rem Extract the file name
for /F %%A in ("%%e") do set "name=%%A"
echo !value! !name!
)
Here's a hybrid batch + JScript script (but still a .bat file) that will perform a regexp replace similar to NextInLine's PowerShell solution.
#if (#CodeSection == #Batch) #then
#echo off
setlocal
set "logfile=test.log"
rem // Ask JScript to parse log. On each line, %%I = hex. %%J = exe.
for /f "tokens=1*" %%I in ('cscript /nologo /e:JScript "%~f0" "%logfile%"') do (
echo %%I %%J
)
rem // End main runtime.
goto :EOF
#end
// JScript chimera portion
var fso = WSH.CreateObject('Scripting.FileSystemObject'),
log = fso.OpenTextFile(WSH.Arguments(0), 1);
while (!log.AtEndOfStream) {
var line = log.ReadLine();
WSH.Echo(line.replace(/^.+(0x[0-9a-f]+) \w:\\.+?\\(\w+\.exe).+$/i, "$1 $2"));
}
log.Close();
Course if I were in your boat I'd probably use GnuWin32 sed.
sed -r -e "s/^.*(0x[a-f0-9]+) \w:.+\\(.+\.exe).*$/\1 \2/i" test.log
Just for giggles, I ran some time tests of each fully-working solution against the O.P.'s test log file above, running each several times and getting the mode duration (the result occurring most often).
Aacini's solution: 0.013s (Excellent, but depends on narrow matches)
sed: 0.015s (simplest)
Magoo's solution: 0.034s (clever!)
my JScript hybrid: 0.034s (the best, of course)
dbenham's jrepl.bat: 0.051s (powerful Swiss army knife solution)
NextInLine's PowerShell: hanged my timer script, but felt like about a half a second after the initial painful priming of PowerShell
This is really a task that calls for regular expressions, and for regular expressions at the windows command-line you want powershell. Fortunately, you can run powershell from a batch file or the DOS command-prompt:
powershell -Command "(Get-Content 'c:\full_path_here\input.log') -replace '.+?(0x[0-9a-f]{3}) .+?\\([^\\]+\.exe).*', '$1 $2'"
This has a few parts
powershell -Command runs the entire expression in quotation marks as though it were run from the powershell command line
Get-Content is like the linux cat command - it reads the entirety of the file contents
-replace uses regular expressions to replace the content on each line of the file with the two matched expressions in parentheses
I want to match all lines of the following text with FINDSTR /R
LABO_A =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = host01)(PORT = 1521))
(CONNECT_DATA =
(SERVICE_NAME = LABO)
)
)
I already tried What are the undocumented features and limitations of the Windows FINDSTR command?
Especially the "Searching across line breaks" part. But unfortunately it didn't work.
My approach is the following:
SETLOCAL
set LF=^
FOR /F %%A IN ('COPY /Z "%~dpf0" NUL') DO SET "CR=%%A"
SETLOCAL enableDelayedExpansion
FINDSTR /R "LABO_A.=.!CR!*!LF!.*(DESCRIPTION.=.!CR!*!LF!.*(ADDRESS.=.(PROTOCOL.=.TCP)(HOST.=.host01)(PORT.=.1521))!CR!*!LF!.*(CONNECT_DATA.=!CR!*!LF!.*(SERVICE_NAME.=.LABO)!CR!*!LF!.*)!CR!*!LF!.*)" %FINDPATH%
Am I missing something? Or is the batch regex simply not powerful enough to realize this?
SOLUTION:
The approach of #dbenham let me reconsider my regex-string. So I edited it to
FINDSTR /R /C:"LABO_A =!CR!*!LF!.*(DESCRIPTION =!CR!*!LF!.*(ADDRESS = (PROTOCOL = TCP)(HOST = host01)(PORT = 1521))!CR!*!LF!.*(CONNECT_DATA =!CR!*!LF!.*(SERVICE_NAME = LABO)!CR!*!LF!.*)!CR!*!LF!.*)" %FINDPATH% > NUL
I removed some unnecessary white spaces and adapted the parameters of FINDSTR.
Now it works.
Your regex is wrong. Your source lines end immediately after the =, but the extra . in your regex is looking for an additional character after the =.
It looks to me you are using . to represent white space. I think you would be better off using actual spaces, but then you need the /C option.
The following matches the lines successfully.
#echo off
SETLOCAL
set LF=^
FOR /F %%A IN ('COPY /Z "%~dpf0" NUL') DO SET "CR=%%A"
SETLOCAL enableDelayedExpansion
FINDSTR /R /C:"LABO_A =!CR!*!LF! *(DESCRIPTION =!CR!*!LF! *(ADDRESS = (PROTOCOL = TCP)(HOST = host01)(PORT = 1521))!CR!*!LF! *(CONNECT_DATA =!CR!*!LF! *(SERVICE_NAME = LABO)!CR!*!LF! *)!CR!*!LF! *)" test.txt
Note that even though all lines in the regex are matched, only the first line of the matching set is printed.
I suspect that the line breaks are not required in your configuration file. Here is another variation that allows for more variation in the white space.
#echo off
setlocal enableDelayedExpansion
set LF=^
FOR /F %%A IN ('COPY /Z "%~dpf0" NUL') DO SET "CR=%%A"
set "ws=[ !cr!!lf!]*"
FINDSTR /RX /C:"LABO_A =!ws!(DESCRIPTION =!ws!(ADDRESS = (PROTOCOL = TCP)(HOST = host01)(PORT = 1521))!ws!(CONNECT_DATA =!ws!(SERVICE_NAME = LABO)!ws!)!ws!)!ws!" test.txt
I also attempted to allow white space in every place I thought possible, but that exceeded FINDSTR's maximum REGEX string length.
Essentially, batch regex isn't powerful enough. SED would be better no doubt.
Nonetheless, here's a way to detect that a sequence of lines appears in a file. It's a little restricted, but should suffice for the sequence you've nominated. It assumes that leading spaces are not significant.
#ECHO OFF
SETLOCAL enabledelayedexpansion
FOR /f "delims==" %%a IN ('set l_ 2^>nul') DO "SET %%a="
SET /a lines=0
FOR /f "tokens=*" %%a IN (q19859936.txt) DO SET /a lines+=1&SET l_!lines!=%%a
SET hits=0
SET "stop="
FOR /f "tokens=*" %%a IN (q19859936.test) DO (
SET l_0=%%~a
CALL :test
IF DEFINED stop GOTO done
)
:done
IF DEFINED stop (ECHO FOUND ) ELSE (ECHO NOT FOUND)
GOTO :EOF
:test
SET /a hits+=1
ECHO IF NOT "!l_%hits%!"=="%l_0%"
IF NOT "!l_%hits%!"=="%l_0%" SET hits=0&IF %hits%==1 (GOTO :eof) ELSE (GOTO test)
IF %hits%==%lines% SET stop=Y
GOTO :eof
[edited code 20131111T1408Z - first FOR had tokens=2]
The initial FOR ensures that variables L_* are cleared.
The file q19859936.txt is read as the line-sequence-to-be-detected data.
q19859936.test is then examined. Each line is assigned to L_0 in turn and the internal subroutine :test will check to see whether it matches the next-line-expected.
The IF NOT statement is significant - and seemingly illogical (you'd need to add the /i switch to make it case-insensitive if you so want...) When batch parses the line, %hits% is replaced by the then-current value of hits and THEN the line is executed, so hits will be reset to 0 if ever a mismatch is found. If the HITS count WAS not 1, then the test is repeated. This takes care of the case
matches line 1
matches line 2
matches line 3
matches line 1
matches line 2
matches line 3
matches line 4
matches line 5
matches line 6
where the second "line 1" is encountered when "line 4" was expected. HITS is thus changed to 0, but it WAS 4 so execution passes back to :test and the test repeated with HITS=1.
Another approach could have been to read lines into another array (say L#*) and test that L_* matched L#*, for %LINES% entries. On no match, ripple-up and assign the next line read to L#!lines! ... but I thought of that later. Probably be easier and better, too - I'll leave it as an exercise for whoever may be interested.
This will work if you are after the LABO_A reference.
It uses a helper batch file called findrepl.bat from - https://www.dropbox.com/s/rfdldmcb6vwi9xc/findrepl.bat
Place findrepl.bat in the same folder as the batch file or on the path.
type "file.txt" | findrepl "^LABO_A =" /e:"^ \)"
From a batch file I want to extract the number 653456 from the following string:
C:\Users\testing\AppData\Local\Test\abc123\643456\VSALBT81_COM
The number will change, however it will always be just digits.
My current theory is to search for something that fits \alldigits\, then replace the two \s with white space, but I can’t quite get it.
Assuming the number is always the parent folder (the folder before the end):
#echo off
set "str=C:\Users\testing\AppData\Local\Test\abc123\643456\VSALBT81_COM"
for %%F in ("%str%\..") do set "number=%%~nxF"
EDIT - Code sample adapted to correct errors shown in comments
set d=C:\Users\testing\AppData\Local\Test\abc123\643456\VSALBT81_COM
for %%f in ("%d:\=" "%") do for /f %%n in ('echo %%f^|findstr /b /e /r "\"[0-9]*\""') do (
echo %%~n
)
Just precede the path with a quote, split the path, replacing each backslash with a quote a space and a quote and append a quote (so we have a list of elements to iterate), and for each part check if it is formed only by numbers
#echo off
setlocal EnableDelayedExpansion
set "string=C:\Users\testing\AppData\Local\Test\abc123\643456\VSALBT81_COM"
for /L %%d in (0,1,9) do set "string=!string:\%%d=\ %%d!"
for /F "tokens=2" %%a in ("%string%") do for /F "delims=\" %%b in ("%%a") do echo Number: [%%b]
This uses a helper batch file called repl.bat from - https://www.dropbox.com/s/qidqwztmetbvklt/repl.bat
#echo off
set "string=C:\Users\testing\AppData\Local\Test\abc123\643456\VSALBT81_COM"
echo "%string%"|repl ".*\\([0-9]*)\\.*" "$1"
Here is how I striped numbers from a string in batch (not a file path, should be generically working for a "string")
#ECHO OFF
::set mystring=Microsoft Office 64-bit Components 2013
set mystring=Microsoft 365 Apps for enterprise - en-us
echo mystring = %mystring%
for /f "tokens=1-20 delims=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!##$&*()-= " %%a in ("%mystring%") do (
IF %%a == 64 (
set ONum=%%b
GoTo varset
)
IF %%a == 32 (
set ONum=%%b
GoTo varset
)
set ONum=%%a
)
:varset
echo numfromalphanumstr = %numfromalphanumstr%
pause
https://www.dostips.com/forum/viewtopic.php?t=3499
https://superuser.com/questions/1065531/filter-only-numbers-0-9-in-output-in-classic-windows-cmd
Extract number from string in batch file
How to extract number from string in BATCH