Powershell5 script choking on special characters in a Windows 10 batch file or is my code fundamentally wrong?
Parse 6800 line Windows 10 batch file, find string {LINE2 1-9999} and replace {1-9999} with the line number the code is on, re-write the batch file. There are 54 instances of {LINE2 1-9999}. If I parse the entire batch the first 54 lines are outputed, none of which contains the string.
$lines = sls "LINE2" $env:windir\_61.bat | Select-Object -ExpandProperty LineNumber
gc $env:windir\_61.bat -OutVariable Content
$output = for ($index = 0; $index -lt $lines.count; $index++) {
$Content[$index] -replace "LINE2.*", "LINE2 $($lines[$index])"
}
$output | sc "$env:TEMP\somebadhat.bat"
If _61.bat is:
TITLE %TIME% NO "%zmyapps1%\*.*" ARCHIVE ATTRIBUTE LINE2 1243
TITLE %TIME% DOC/SET YQJ8 LINE2 1887
SET ztitle=%TIME%: WINFOLD LINE2 2557
TITLE %TIME% _*.* IN WINFOLD LINE2 2597
TITLE %TIME% %%ZDATE1%% YQJ25 LINE2 3672
TITLE %TIME% FINISHED. PRESS ANY KEY TO SHUTDOWN ... LINE2 4922
Results:
TITLE %TIME% NO "%zmyapps1%\*.*" ARCHIVE ATTRIBUTE LINE2 1
TITLE %TIME% DOC/SET YQJ8 LINE2 2
SET ztitle=%TIME%: WINFOLD LINE2 3
TITLE %TIME% _*.* IN WINFOLD LINE2 4
TITLE %TIME% %%ZDATE1%% YQJ25 LINE2 5
TITLE %TIME% FINISHED. PRESS ANY KEY TO SHUTDOWN ... LINE2 6
If _61.bat is:
TITLE %TIME% NO "%zmyapps1%\*.*" ARCHIVE ATTRIBUTE LINE2 1243
TITLE %TIME% DOC/SET YQJ8 LINE2 1887
SET ztitle=%TIME%: WINFOLD LINE2 2557
TITLE %TIME% _*.* IN WINFOLD LINE2 2597
TITLE %TIME% %%ZDATE1%% YQJ25 LINE2 3672
TITLE %TIME% FINISHED. PRESS ANY KEY TO SHUTDOWN ... LINE2 4922
:: Microsoft Windows [Version 10.0.17134.345] 64-BIT
@ECHO OFF
TITLE %TIME% FINISHED. PRESS ANY KEY TO SHUTDOWN ... LINE2 4923
Results:
TITLE %TIME% NO "%zmyapps1%\*.*" ARCHIVE ATTRIBUTE LINE2 1
TITLE %TIME% DOC/SET YQJ8 LINE2 2
SET ztitle=%TIME%: WINFOLD LINE2 3
TITLE %TIME% _*.* IN WINFOLD LINE2 4
TITLE %TIME% %%ZDATE1%% YQJ25 LINE2 5
TITLE %TIME% FINISHED. PRESS ANY KEY TO SHUTDOWN ... LINE2 6
:: Microsoft Windows [Version 10.0.17134.345] 64-BIT
@ECHO OFF
TITLE %TIME% FINISHED. PRESS ANY KEY TO SHUTDOWN ... LINE2 4923
is missing.
There are 30 instances of @. For example:
SET p= & title NetFix v1.0a by Giovanni Heward (g@utahjrs.com) complete!
%PROGRAMFILES%\7-Zip\7z.exe" u -t7z -x@"%WINDIR%\_exclude7z.txt"
Is that causing the problem? What would I replace it with? Why, if i parse the entire file, that has 54 instances of the string, would it output 54 lines, none of which contains the string? Is there something fundamentally wrong with my code?
You're script will only parse the number of lines of your input script that contain the "LINE2 nnnn" token. So in your second input example above, out of the 9 lines of input, only 7 contain "LINE2 nnnn", so you only get the first 7 lines processed. The reason is this:
$lines = sls "LINE2" c:\temp\_61.bat | Select-Object -ExpandProperty LineNumber
This line contains an array of all the lines that contain the matching text, so $lines
is an array of 7 integers. Later in your for loop, you are only iterating over that many items:
$output = for ($index = 0; $index -lt $lines.count; $index++) {
You don't really need the $lines
array, just use the loop index over the content to determine the line number:
gc $env:windir\_61.bat -OutVariable Content
$output = for ($index = 0; $index -lt $Content.count; $index++) {
$Content[$index] -replace "LINE2.*", "LINE2 $($index)"
}
$output | sc "$env:TEMP\somebadhat.bat"
Note, you can clean up your script a bit by removing the temporary variables, by pushing all the data through the pipeline:
gc $env:windir\_61.bat | foreach -begin {$lc = 1} -process {
$_ -replace "LINE2 \d*", "LINE2 $lc";
$lc += 1
} | out-file "$env:TEMP\somebadhat.bat"
Just to visualize what Select-String returns and how to replace the linenumber in the index based file content (zdan's approach is much more efficient)
## Q:\Test\2019\02\15\SO_54712715.ps1
$FilePath = '.\_61v2.bat'
$FileContent = Get-Content $FilePath
$FileMatches = Select-String -Path $FilePath -Pattern '(?<=LINE2 )\d+\s?$'
ForEach ($FM in $FileMatches){
"{0}:{1} [{2}=>{0}]" -f $FM.LineNumber,$FM.Line,$FM.Matches.Value
$FileContent[$FM.LineNumber-1] = $FM.Line.Replace($FM.Matches.Value,$FM.LineNumber)
}
$FileContent | Set-Content $FilePath
Sample output:
> Q:\Test\2019\02\15\SO_54712715.ps1
1:TITLE %TIME% NO "%zmyapps1%\*.*" ARCHIVE ATTRIBUTE LINE2 1243 [1243=>1]
2:TITLE %TIME% DOC/SET YQJ8 LINE2 1887 [1887=>2]
3:SET ztitle=%TIME%: WINFOLD LINE2 2557 [2557=>3]
4:TITLE %TIME% _*.* IN WINFOLD LINE2 2597 [2597=>4]
5:TITLE %TIME% %%ZDATE1%% YQJ25 LINE2 3672 [3672=>5]
6:TITLE %TIME% FINISHED. PRESS ANY KEY TO SHUTDOWN ... LINE2 4922 [4922=>6]
9:TITLE %TIME% FINISHED. PRESS ANY KEY TO SHUTDOWN ... LINE2 4923 [4923=>9]
and resulting file content (version 2 of above bat)
> gc .\_61v2.bat
TITLE %TIME% NO "%zmyapps1%\*.*" ARCHIVE ATTRIBUTE LINE2 1
TITLE %TIME% DOC/SET YQJ8 LINE2 2
SET ztitle=%TIME%: WINFOLD LINE2 3
TITLE %TIME% _*.* IN WINFOLD LINE2 4
TITLE %TIME% %%ZDATE1%% YQJ25 LINE2 5
TITLE %TIME% FINISHED. PRESS ANY KEY TO SHUTDOWN ... LINE2 6
:: Microsoft Windows [Version 10.0.17134.345] 64-BIT
@ECHO OFF
TITLE %TIME% FINISHED. PRESS ANY KEY TO SHUTDOWN ... LINE2 9
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.