Nested while loop only runs once - powershell

I am trying to use this script to add text to every file in a folder. It is working, but only on the first file in the folder. The for statement seems to be working because it is giving an accurate count of the files in the folder, but only modifying the first file. I feel like I am missing something stupid here.
$fullPath = "M:\BHX\DrillTeqConversion"
$reader = [System.IO.File]::OpenText("M:\BHX\DrillteqConversion.txt")
$lineNumberx = 25
function get200Files($path) {
$mprFiles = #(Get-ChildItem $path -include *.mpr -Recurse)
if ($mprFiles -ne $NULL) {
$mprFileCount = 0
For ($i = 0; $i -lt $mprFiles.Length; $i++) {
$mprFileCount += 1
$thisFile = $mprFiles[$i]
while($null -ne ($line = $reader.ReadLine())) {
$textToAdd = $line
$newLineToAdd = "`n"
$fileContent = Get-Content $thisFile
$fileContent[$lineNumberx-1] += $newLineToAdd
$fileContent[$lineNumberx-1] += $textToAdd
$fileContent | set-Content $thisFile
$lineNumberx = $lineNumberx + 1
}
}
Write-Host ("A total of " + $mprFileCount + " files were converted.")
}
}
get200Files $fullPath

[System.IO.File]::OpenText("M:\BHX\DrillteqConversion.txt") with ReadLine() only allows reading through a file once. So you cannot loop through the file endlessly without reopening it or using another strategy. Code samples below have been reduced for simplicity.
for ($i = 0; $i -lt $mprFiles.Length; $i++) {
# for loop code before while loop
$reader = [System.IO.File]::OpenText("M:\BHX\DrillteqConversion.txt")
while($null -ne ($line = $reader.ReadLine())) {
# while loop code
}
# After while loop but inside of For loop
$reader.Dispose()
$reader.Close()
}
You could make a new parameter for your function and pass that into OpenText() as well.
function get200Files($path,$FileToRead) {
for ($i = 0; $i -lt $mprFiles.Length; $i++) {
# for loop code before while loop
$reader = [System.IO.File]::OpenText($FileToRead)
while($null -ne ($line = $reader.ReadLine())) {
# while loop code
}
# After while loop but inside of For loop
$reader.Dispose()
$reader.Close()
}
}
$fullPath = "M:\BHX\DrillTeqConversion"
$ReaderPath = "M:\BHX\DrillteqConversion.txt"
get200Files $fullPath $ReaderPath

Related

Check for strong string matching "powershell"

I get an element from the array, I check whether the line exists in the file or not. If it exists, then I change it to an element from the second array by index. Everything works fine. The problem is that if a string from an array is similar to a string in a file, then it is considered that it is in the file. For example, "#TokenUri" and "#Token" are treated the same. The "$initial_array" variable is taken from the vault secret store. Added to the code for ease of debugging. All keys and values are a random set of characters, close to reality. It is necessary to look for a strict one-to-one correspondence.
$pathFile = 'D:\Git\Gitlab\powershell\appsettings.json'
$array_key = #()
$array_value = #()
$initial_array = "#{#TokenUri=ovnsinv-iovi0ew-dvoiw9; #User=Vasia; #Timeout=00:00:30; #ExternalServices=gdgdfg; #Password=xvnen834n9; #ApplicationName=Tupoe; #Uri=https://gitlab.com/}" -replace '[#,{,},;]'
$array= $initial_array.Split('=').Split(' ')
for ($i = 0; $i -le $array.Count; $i++) {
if ( ($i % 2) -eq 0) {
$array[$i] | ForEach-Object { $array_key+=$_ }
}
else {
$array[$i] | ForEach-Object { $array_value+=$_ }
}
}
for ($i=0; $i -lt $array_key.Length; $i++) {
if ((Get-Content -Path $pathFile) -match $array_key[$i]) {
(Get-Content -Path $pathFile) -replace $array_key[$i], $array_value[$i] | Set-Content -Path $pathFile
}
else {
$element = $array_key[$i]
throw "Element $element is missing from the file - $pathFile"
break
}
}

Reducing amout of lines in variable within loop in Powershell

I have a txt file containing 10000 lines. Each line is an ID.
Within every loop iteration I want to select 100 lines, put them in a special format and do something. I want to do this until the document is finished.
The txt looks like this:
406232C1331283
4062321N022075
4062321H316457
Current approach:
$liste = get-content "C:\x\input.txt"
foreach ($item in $liste) {
azcopy copy $source $target --include-pattern "*$item*" --recursive=true
}
The system will go throug the TXT file and make a copy request for every name it finds in the TXT file. Now the system is able to handle like 300 search-patterns in one request. like
azcopy copy $source $target --include-pattern "*id1*;*id2*;*id3*"
How can I extract 300 items from the document at once, separate them with semicolon and embedd them in wildcard? I tried to pipe everyting in a variable and work with -skip.
But it seems not easy to handle :(
Use the -ReadCount parameter to Get-Content to send multiple lines down the pipeline:
Get-Content "C:\x\input.txt" -ReadCount 300 | ForEach-Object {
$wildCards = ($_ | ForEach-Object { "*$_*" } -join ';'
azcopy copy $source $target --include-pattern $wildCards --recursive=true
}
Do you want 100 or 300 at a time? ;-)
I'm not sure if I really got what the endgoal is but to slice a given amount of elements in chunks of a certain size you can use a for loop like this:
$liste = Get-Content -Path 'C:\x\input.txt'
for ($i = 0; $i -lt $Liste.Count; $i += 100) {
$Liste[$i..$($i + 99)]
}
Now if I got it right you want to join these 100 elements and surround them with certain cahrachters ... this might work:
'"*' + ($Liste[$i..$($i + 99)] -join '*;*') + '*"'
Together it would be this:
$liste = Get-Content -Path 'C:\x\input.txt'
for ($i = 0; $i -lt $Liste.Count; $i += 100) {
'"*' + ($Liste[$i..$($i + 99)] -join '*;*') + '*"'
}
There's many ways, here's one of them...
First I would split array to chunks of 100 elements each, using this helper function:
Function Split-Array ($list, $count) {
$aggregateList = #()
$blocks = [Math]::Floor($list.Count / $count)
$leftOver = $list.Count % $count
for($i=0; $i -lt $blocks; $i++) {
$end = $count * ($i + 1) - 1
$aggregateList += #(,$list[$start..$end])
$start = $end + 1
}
if($leftOver -gt 0) {
$aggregateList += #(,$list[$start..($end+$leftOver)])
}
$aggregateList
}
For example to split your list into chunks of 100 do this:
$Splitted = Split-Array $liste -count 100
Then use foreach to iterate each chunk and join its elements for the pattern you need:
foreach ($chunk in $Splitted)
{
$Pattern = '"' + (($chunk | % {"*$_*"}) -join ";") + '"'
azcopy copy $source $target --include-pattern $Pattern --recursive=true
}

Powershell Open File, Edit File, Update File - document lock issue

I have a text file with a list of multiple files that exceeded x characters. What I am trying to do is open each file, scan each line of the file, and if a file is more than x characters long I move the line to the next line so the file does not exceed x characters. That piece works great. The problem I am having is updating the text file I am trying to change/edit. I suspect the lock is the powershell script since the script is reading the file. Does anyone have any ideas on what I can do to update the original text file or remove the lock? Thanks for any help! My code is below:
[int] $limit = 131
$path = get-content C:\document\fix.txt
foreach ($f in $path)
{
Get-Content -path $f |
ForEach-Object {
$line = $_
for ($i = 0; $i -lt $line.Length; $i += $limit)
{
$length = [Math]::Min($limit, $line.Length - $i)
$line.SubString($i, $length)
}
} |
Set-Content $f
}
I figured it out! I had to put ( ) around get-content. Basically means - finish what you are doing before going to the next step.
[int] $limit = 131
$path = get-content C:\Soarian\fix.txt
foreach ($f in $path)
{
(Get-Content -path $f) |
ForEach-Object {
$line = $_
for ($i = 0; $i -lt $line.Length; $i += $limit)
{
$length = [Math]::Min($limit, $line.Length - $i)
$line.SubString($i, $length)
}
} |
Set-Content $f -Force
}

Powershell - .csv split strings

I am trying to use powershell for extracting email addresses from a .csv file.
Each row in the .csv may have none or more emails separated by ",".
f.e.
Email
info#domain.com, email#domain.com, person#contonso.com
something#domain.com
My goal is to write it that way so I can get the "info#" from the row if it is present + 1 extra email from the row if it is present. If there is no "info#" get at least 1 or 2 emails from that row.
Here is the fracture of the code, where I am manually able to say on what position is what email, but I am not able to get this part to work in the for cycle which I could use to enumerate the number of occurences as it appears I cannot convert it to int at all.
$Occurrences = $email.Split(",").GetUpperBound(0);
[int]$Occurrences
$data = Import-Csv -path $path
foreach($contact in $data)
{
$email = $contact.Email
if($email.Contains("info"))
{
$emailSplit = $contact.Email.Split(",")
$Occurrences = $email.Split(",").GetUpperBound(0);
[int]$Occurrences
$name = $domainSplit[0]
for([int]$i = 0;-lt $Occurrences.ToInt32(); $i++)
{
}
}
}
Any help is appreciated.
This is not a valid CSV Format. Cant you export the data via JSON from the datasource?
You need to split the single lines and then do your operations
$data = Get-Content -path $path
for($i=1; $i -lt $data.Length; $i++)
{
$emailSplit = [array]$data[$i].Split(",")
for($j = 0; $j -lt $emailSplit.Length; $j++) {
<#do your operation here...
loop once through the elements, check for info#, and then assign them accoringly...
#>
}
}
V2:
$data = Get-Content -path $path
for($i=1; $i -lt $data.Length; $i++)
{
$emailSplit = [array]$data[$i].Split(",")
Write-Host ('Results for line: ' + $i)
$infoFound = $false
for($j = 0; $j -lt $emailSplit.Length; $j++) {
if($emailSplit[$j] -match 'Info#*') {
$infoFound = $true
$infoPos = $j
}
}
[array]$results = $emailSplit[0]
$results += $emailSplit[-1]
if($infoFound) {
if($infoPos = 0) {$results[1] = $emailSplit[$infoPos]}
else {$results[0] = $emailSplit[$infoPos]}
}
Write-Host ('Element1: ' + $results[0] + ' Element2: ' + $results[1])
}

Keep lines from line X then delete others if does not contain pattern

I am trying to manipulate a textfile. I want it to keep the first X numbers of lines and after that it should look for a string pattern. If a line contains the pattern it should be kept otherwise deleted.
I got both things to work separately but not together. It works to keep lines until X and remove the rest. And I got it to work to remove all lines except for lines with a pattern, but I can't get it to work for both together.
EDIT: here is the code:
$data = Get-Content test.md
$newdata = ""
$n = 0
Foreach ($line in $data) {
if ($n++ -ge 6) {
$newdata += $line | Where{$_ -match '\[R\]'}
} else {
$newdata += $line
}
$newdata += " `r`n"
}
$newdata > test2.md
The problem is the lines are still there as empty lines. But they should be completely deleted.
$data = Get-Content test.md
$newdata = ""
$n = 0
Foreach ($line in $data) {
if ($n++ -gt 6) {
if ($line -match '\[R\]') {
$newdata += $line + " `r`n"
}
} else {
$newdata += $line + " `r`n"
}
}
$newdata > test2.md
got it to work like that.
You could use
"test.md" | % {
Get-Content $_ -TotalCount 6
(Select-String -path $_ -match '\[R \]' -AllMatches).Line
} | Out-File test2.md -Encoding Ascii