Powershell - .csv split strings - powershell

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])
}

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
}
}

How to prevent random output?

When I run my script it adds an output I haven't requested.
$input = Read-Host -p "Message?";
$morseFile = Get-Content C:\Users\lukas.downes\Documents\Script\morse.csv
$letter;
$code;
$outputAsString = "";
for ($j = 0; $j -lt $input.Length; $j++)
{
for ($i = 1; $i -lt $morseFile.Length; $i++)
{
$letter = ([string]$morseFile[$i]).Split(',')[0];
$code = ([string]$morseFile[$i]).Split(',')[1];
if ($input[$j] -eq $letter)
{
$outputAsString += $code;
write-host ("" + $letter + ": " + $code)
}
}
}
Write-Host $outputAsString
and this outputs:
> Message?: sos
> Z
> --..
> S: ...
> O: ---
> S: ...
> ...---...
which is rather weird, as I don't even use a "Z" in my script.
If your input csv morse.csv looks anything like this:
Letter,Code
O,---
P,.--.
Q,--.-
R,.-.
S,...
Then this should work for you:
# import the csv as array of objects
$codes = Import-Csv -Path 'C:\Users\lukas.downes\Documents\Script\morse.csv'
# for more speed, change this array of objects into a lookup Hashtable
$morse = #{}
foreach ($item in $codes) {
$morse[$item.Letter] = $item.Code
}
$inputString = Read-Host -p "Message?"
if (![string]::IsNullOrWhiteSpace($inputString)) {
$inputString.ToUpper().ToCharArray() | ForEach-Object {
'{0}: {1}' -f $_, $morse[$_.ToString()] # or: $morse["$_"]
}
}
When entering 'sos', the output is:
S: ...
O: ---
S: ...
P.S. Don't use $input as self-defined variable, because it is an Automatic variable

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
}

Nested while loop only runs once

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

Powershell sorting array into multi-dimensional array with the “-like” comparison operator

I'am trying to collect my array into a multidimensional array by using the -like comparison operator for further processing.
I wrote the following array loop but i cannot replace "*kw1*" with "*$keyword[$j]*". It will break the operator validation.
$keywords = #("kw1", "kw2")
$list = #("name_kw1_000", "name_kw1_001", "name_kw1_002", "name_kw2_000", "name_kw2_001", "name_kw2_002")
$mdarr= New-Object object[][] $keywords.Length
for ($i = 0; $i -lt $list.Length; ++$i) {
for ($j = 0; $j -lt $keywords.Length; ++$j) {
if ( $list[$i] -like "*kw1*" ) {
$mdarr[$j] += $list[$i];
}
}
}
My expected output is:
$mdarr[0]
name_kw1_000
name_kw1_001
name_kw1_002
$mdarr[1]
name_kw2_000
name_kw2_001
name_kw2_002
Is this possible with the above array loop or would i have to do this completely different since the -like operator does not seem to be array friendly.
I think you mean to get output for a variable length array, using more keywords.
As montonero comments, you never test if the keyword is actually part of the item in the list of words.
Maybe this will help:
# there is no need to enclose the items with '#()'
$keywords = "kw1", "kw2"
$list = "name_kw1_000", "name_kw1_001", "name_kw1_002", "name_kw2_000", "name_kw2_001", "name_kw2_002"
# in case your keywords contain characters that have special meaning
# for regex '-match', we should escape these characters.
$keywords = $keywords | ForEach-Object { [RegEx]::Escape($_) }
# fill the object array
$mdarr= New-Object object[][] $keywords.Count
for ($i = 0; $i -lt $keywords.Count; $i++) {
foreach ($item in $list) {
if ($item -match $keywords[$i]) {
$mdarr[$i] += $item
}
}
}
# write result
for ($i = 0; $i -lt $mdarr.Count; $i++) {
Write-Host ("`$mdarr[$i]") -ForegroundColor Yellow
$mdarr[$i]
}
This will output
$mdarr[0]
name_kw1_000
name_kw1_001
name_kw1_002
$mdarr[1]
name_kw2_000
name_kw2_001
name_kw2_002