parsing script never ends - powershell

i have the following script but it does never end executing.
what could be the issue ? I tried to debug it but apparently it works correctly with a single file, but when I throw it against a folder full of content fails.
$path = split-path -parent $MyInvocation.MyCommand.Definition
$files = Get-ChildItem "$path\CodeForCertification\5_SourceCode\*" -Include *.c,*.h -Recurse | where{
! $_.PSIsContainer
}#$PSScriptRoot
ForEach ($file in $files){
$data = Get-Content -Path $file.FullName
$feature = Get-Content "$path\Disabled_Features.txt"
#[System.ArrayList]$Modifier
$nl=[Environment]::NewLine
$Modifier=#()
$flag=0
$data = $data | ForEach-Object -Begin {
$ignore = $false; $levels = 0
} -Process {
for($counter=0; $counter -lt $feature.Count; $counter++){
$parse = $feature[$counter]
if($_ -match "^#ifdef $parse" -And $flag -eq '0') {
$ignore = $true
$flag = 1;
}
}
if($ignore) {
if ($_ -match "^#ifdef") {
$levels++
}elseif ($_ -match "#endif") {
if($levels -ge 1) {
$levels--
if($levels -eq '0'){
$ignore = $false
}
}
}
}else {
$flag=0
$temp=$_
$_
$Modifier+="$temp"
}
}
$data | Out-File $file.FullName
}

OK, Jackson, let's solve your problem before you enter some kind of question spam filter ;-)
Consider this (just put it somewhere at the start of your script):
function RemoveUndesiredFeatures([string[]]$lines,[string[]]$undesiredFeatures)
{
$inIgnoreBlock = $false
$nestingLevel = 0
foreach ($line in $lines)
{
if ($inIgnoreBlock)
{
# Only search for nested blocks and end of block
if ($line -like "#ifdef*")
{
$nestingLevel++
}
elseif ($line -like "#endif*")
{
$nestingLevel--
}
if ($nestingLevel -eq 0)
{
$inIgnoreBlock = $false
}
}
else
{
# Search for undesired feature
$isIfdefMatch = $line -match "#ifdef (?<feature>\w+)"
if ($isIfdefMatch -and ($Matches.feature -in $undesiredFeatures))
{
# Ignore Feature
$inIgnoreBlock = $true
$nestingLevel++
}
else
{
# Output line
$line
}
}
}
}
Here is my example to use it:
$undesiredFeatures = #("F1","F2") # Just as example. Get-Content on a file with features is also fine
$files = Get-ChildItem *.c,*.h -Recurse # Again, just as example
foreach ($file in $files)
{
$lines = Get-Content $file.FullName
$changedLines = RemoveUndesiredFeatures $lines $undesiredFeatures
if ($changedLines.Count -ne $lines.Count)
{
# Features were removed. Write out changed file (to a different file to preserve my test files)
Set-Content -Value $changedLines -Path "$($file.FullName).changed"
}
}

Related

My use of Out-File and/or [system.io.file]::WriteAllText

Both commands work when running inside Visual Studio Code but if I execute the ps1 file directly in pwsh 7 the file is blank. Same if I right-click and select Open With pwsh.
Tried both commands, out-file first but moved to [system.io.file]::WriteAllText to remove blank rows. Both successfully create and populate a csv file as expected when I execute it from within Visual Studio Code.
try {
$files = #()
$files = Get-ChildItem -Path "\\networkShare\ps1files"
#$files = $files + (get-childitem -Path "\\networkShare\ps1files2")
$results = foreach ($file in $files) {
if ($file.FullName.substring($file.FullName.length - 3, 3) -eq "ps1") {
#Write-Host $file
$scriptblock = [scriptblock]::Create((Get-Content -Raw -Path $file.FullName))
$ast = $scriptblock.Ast
$commands = $ast.FindAll( { $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true)
$commandText = foreach ($command in $commands) {
$command.CommandElements[0].Extent.Text
}
$commandText |
Select-Object -Unique |
Sort-Object |
Select-Object #{
Label = "Module"
Expression = { (Get-Command $_).Source.Substring(0, (Get-Command $_).Source.Length) }
}
$value = ""
foreach ($result in $results) {
if ($result.Module -inotlike "C:*") {
if ($value -inotlike "*" + $result.Module + "*") {
$value = $value + $result.Module + "`n"
}
}
}
}
}
$ModulesInUse = #()
$NewModules = #()
$NewestMod = #()
#$NewestMod = ""
$ModulesInUse = Import-Csv \\networkShare\PSModulesUsed.csv -Header "Module"
$NewModules = $results.Where({ $_.module -notin $ModulesInUse.Module -and $_.Module.Length -gt 0 -and $_.Module -inotlike "C:*" })
if ($NewModules.count -gt 0) {
Write-Host "New Modules found `n"
# Create unique array of new modules found
foreach ( $mod in $NewModules ) {
if ($mod.Module -notin $NewestMod.Module) {
$NewestMod = $NewestMod + $mod
}
}
$NewestMod.module
Write-Host `n
$Prompt = 'Do you want to update the Modules Used csv file now? Y/N'
$UpdateFile = Read-Host -Prompt $Prompt
if ($UpdateFile -eq 'y') {
#$value | Out-File "\\networkShare\PSModulesUsed.csv"
[system.io.file]::WriteAllText(“\\networkShare\PSModulesUsed.csv”, $value)
}
}
else {
Read-Host "No new Modules found. Press any key to exit"
}
Big thanks to mklement0!!!
Making that change to Code revealed the $value I was sending was not correct, even though it was working before. When running outside of Code it was blank.
I moved the code below to outside the $Results block of code and it works now.
$value = ""
foreach ($result in $results) {
if ($result.Module -inotlike "C:*") {
if ($value -inotlike "*" + $result.Module + "*") {
$value = $value + $result.Module + "`n"
}
}
}

How to modify Firefox config (pref.js) line using Powershell

Modifying the Mozilla Firefox Pref.js file using PowerShell.
Need to modify the pref.js file line to reflect a new value
(Hence, instead of any value under user_pref("network.automatic-ntlm-auth.trusted-uris", "*"); to show user_pref("network.automatic-ntlm-auth.trusted-uris", ".abc.com,.abcd.com,.abcde.com") or just add changes after the *
$TopDir = "$env:APPDATA\Mozilla\Firefox\Profiles"
$FileName = 'prefs.js'
$DefaultProfileDir = (Get-ChildItem -LiteralPath $TopDir -Directory).
Where({
$_.FullName -match '\.default'
}).FullName
$FullFileName = Join-Path -Path $DefaultProfileDir -ChildPath $FileName
$data = foreach($line in Get-Content $FullFileName)
{
if($line -contains 'user_pref("network.automatic-ntlm-auth.trusted-uris".*')
{
$line -replace '*' , 'user_pref("network.automatic-ntlm-auth.trusted-uris", ".abc.com,.abcd.com,.abcde.com");'
}
else
{
$line
}
}
$data | Set-Content C:\testfolder\test2.txt
Nelson,
I didn't have your line in my FF profile but I got the code to work on another line so you should be able to modify it to work with yours.
$TopDir = "G:\Test"
$FileName = 'prefs.js'
$FullFileName = "$TopDir\$FileName"
Clear-Host
$Data = foreach($line in Get-Content $FullFileName) {
if( ($line.IndexOf('browser.bookmarks.restore_default_bookmarks')) -ne -1)
{
If ($line.IndexOf('false') -ne -1) {
$line = $line.replace( 'false' , 'true')
}
}
$line
}
$data | Set-Content $("$TopDir" + "\prefs2.js")
I think your code would be:
$TopDir = "$env:APPDATA\Mozilla\Firefox\Profiles"
$FileName = 'prefs.js'
$DefaultProfileDir = (Get-ChildItem -LiteralPath $TopDir -Directory).
Where({
$_.FullName -match '\.default'
}).FullName
$FullFileName = Join-Path -Path $DefaultProfileDir -ChildPath $FileName
$Data = foreach($line in Get-Content $FullFileName) {
if( ($line.IndexOf('network.automatic-ntlm-auth.trusted-uris')) -ne -1)
{
If ($line.IndexOf('*') -ne -1) {
$line = $line.replace( '*' , 'abc.com,.abcd.com,.abcde.com')
}
}
$line
}
$data | Set-Content C:\testfolder\test2.txt
Note: -contains is designed for lists.
HTH
I extended it to be easier to use any property
Input
user_pref("foo", "bar")
user_pref("network.automatic-ntlm-auth.trusted-uris", "*")
Output
user_pref("foo", "bar")
user_pref("network.automatic-ntlm-auth.trusted-uris", ".abc.com,.abcd.com,.abcde.com")
$lines = 'user_pref("foo", "bar")', 'user_pref("network.automatic-ntlm-auth.trusted-uris", "*")'
# multi-line regex for readability
$regex = '(?x)
(?<prefix>user_pref\()
(?<name>
".*"
)
\s*,\s*
(?<value>
".*"
)
(?<suffix>.*)
'
"`ninput:"
$lines
"`noutput:"
$lines | ForEach-Object {
if($_ -match $regex) {
if($matches.name -eq '"network.automatic-ntlm-auth.trusted-uris"') {
$_ = '{0}{1}, {2}{3}' -f #(
$Matches.prefix,
$Matches.name,
'".abc.com,.abcd.com,.abcde.com"',
$Matches.suffix
)
}
}
$_
}

Find and Replace multi-line string

I'm trying to find a string and add the string needed for a program.
I need the code to look to see if the action = run fast already exists and if so do nothing.
$Input = GetContent "${Env:ProgramFiles}\myprogram\file.conf"
$replace = #"
[MyAction_Log]
action = run fast
"#
$Input -replace ('action = run fast') -replace ('\[MyAction_Log\]',$replace) | set-content "${Env:ProgramFiles}\myprogram\file.conf"
I would check before wantonly replacing things you think exist. Also, never use $Input as a variable name; it's an automatic variable and won't do what you think it will (treat it as read-only).
$path = "$Env:ProgramFiles\prog\file.conf"
$file = Get-Content -Path $path
$replacementString = #'
[MyAction_Log]
action = run fast
'#
if ($file -notmatch 'action\s=\srun\sfast')
{
$file -replace '\[MyAction_Log\]', $replacementString |
Set-Content -Path $path
}
An alternative that's able to cope with the action key being located anywhere in the [MyAction_Log] section
$Inside = $False
$Modified = $False
$Path = "$( $env:ProgramFiles )\prog\file.conf"
$NewLines = Get-Content $Path |
ForEach-Object {
if( $_.Trim() -like "[*]" ) { $Inside = $False }
if( $_.Trim() -like "*[MyAction_Log]*" ) { $Inside = $True }
If( $Inside -and $_ -like "action = *" -and $_ -notlike "*run fast*" ) { $Modified = $True; "action = run fast" } else { $_ }
}
If( $Modified ) { $NewLines | Set-Content $Path }

Retrieve matching strings from two text files

I have a text file file_paths.txt that contains full paths on each line:
C:\MyFolder1\app1.exe
C:\MyFolder2\l1.dll
C:\MyFolder3\app2.exe
C:\MyFolder1\l2.dll
C:\MyFolder5\app3.exe
C:\MyFolder3\app4.exe
C:\MyFolder6\app5.exe
I also have file folders.txt that contains list of folders:
C:\MyFolder1
C:\MyFolder2
C:\MyFolder3
C:\MyFolder4
C:\MyFolder8
I need to iterate through the list of folders in folders.txt, match it with files in file_paths.txt and write the results to a file result.txt like this:
In C:\MyFolder1 more than one files has been found:
C:\MyFolder1\app1.exe
C:\MyFolder1\l2.dll
In C:\MyFolder2 one file has been:
C:\MyFolder2\l1.dll
In C:\MyFolder3 more than one files has been found:
C:\MyFolder3\app2.exe
C:\MyFolder3\app4.exe
In C:\MyFolder4 no files has been found.
In C:\MyFolder8 no files has been found.
My attempt that doesn't work:
$paths = [System.IO.File]::OpenText("file_paths.txt")
$folders = [System.IO.File]::OpenText("folders.txt")
$result = "result.txt"
try {
for(;;) {
$folder = $folders.ReadLine()
if ($folder -eq $null) { break }
"In ">> $folder >> ": `n" >> $result
for(;;) {
$path = $paths.ReadLine()
if ($path -eq $null) { break }
if ($path -contains $folder) {" ">>$path>>"`n">>$result }
}
}
} finally {
$paths.Close()
$folders.Close()
}
I would separate processing from reporting. First build a hashtable from the contents of folders.txt and add the lines from file_paths.txt to the matching keys:
$folders = #{}
Get-Content 'folders.txt' | ForEach-Object { $folders[$_] = #() }
Get-Content 'file_paths.txt' | ForEach-Object {
$line = $_
$($folders.Keys) | Where-Object {
$line -like "$_*"
} | ForEach-Object {
$folders[$_] += $line
}
}
Then you can output the resulting data structure like this:
$folders.Keys | ForEach-Object {
'In {0} {1} files have been found' -f $_, $folders[$_].Count
if ($folders[$_].Count -gt 0) {
$folders[$_] | ForEach-Object { "`t$_" }
}
} | Out-File 'result.txt'
Below is a script you can use to do exactly what you need.
Note the $folderPath and $filePath variables. Replace with absolute or relative (to where you execute the script) path of the file_paths.txt and folders.txt files.
$folderPath = 'folders.txt'
$filePath = 'file_paths.txt'
(Get-Content $folderPath).Split('`r`n') | ForEach-Object {
$folder = $_
$count = 0
$fileArray = #()
(Get-Content $filePath).Split('`r`n') | ForEach-Object {
$file = $_
if( $file | Select-String $folder -Quiet ) {
$count++
$fileArray += $file
}
}
if($count -ne 0) {
Write-Output "In $folder, $count files has been found."
$fileArray | ForEach-Object {
Write-Output "`t$_"
}
} else {
Write-Output "In $folder, no files has been found."
}
}

Add Content in Powershell

I need to search through multiple files and underneath specific lines i need to insert lines referenced previously in each respective file. So far i cannot get my script to work at all.
This is what i have so far :
$TextLocation = "M:\test"
$files = get-childitem -filter *.gto -path $TextLocation
Foreach ($file in $files) {
$pagetitle = "DS_PGSEQ-DC:"
$a = Get-Content $file.FullName | Select-String "AssignedToUserID-TZ"
$b = Get-Content $file.FullName | Select-String "EFormID-TZ"
Foreach ($line in $file)
{
if([String]$line -eq "DS_PGSEQ-DC:0001")
{
}
elseif([String]$line -eq $pagetitle)
{
Add-Content $file.FullName ($a -and $b)
}
}
}
There are two common ways for inserting text into a text file:
Process the input file(s) line-by-line, write the output to a temporary file and replace the input file(s) with them temp file afterwards.
for ($file in $files) {
$filename = $file.FullName
Get-Content $filename | % {
if ( $_ -match 'seach pattern' ) {
$_
"new line"
}
} | Out-File $tempfile
MoveItem $tempfile $filename -Force
}
Read the entire content of the file(s), insert the text by using a regular expression replacement, and write the modified content back to the file(s).
for ($file in $files) {
$text = [System.IO.File]::ReadAllText($file.FullName)
$text -replace '.*search pattern.*', "`$0`nnew line" |
Out-File $file.FullName
}
$TextLocation = "M:\test"
$Outputlocation = "M:\test\output"
$files = get-childitem -filter *.gto -path $TextLocation
Foreach ($file in $files) {
$pattern = "\d\d\d[2-9]"
$found=$false
$contains=$false
$lines = Get-Content($file.FullName) |
Foreach-object {
if ($_ -match "AssignedToUserID-TZ") {
$a = $_
}
if ($_ -match "EFormID-TZ") {
$b = $_
}
if ($_ -match "DS_PGSEQ-DC:$pattern") {
if($found -eq $false) {
$found=$true
}
} else {
$found=$false
}
if ($found -eq $true) {
$contains=$true
#Add Lines after the selected pattern
$_
$a
$b
}
if ($found -ne $true) {
$_
}
} | Set-Content($Outputlocation + "\" + $file.Name)
}