How to prevent random output? - powershell

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

Related

Powershell foreach loop through a list that is split from user input

I have this here for loop:
$fromInput = 1
$toInput = 99
for ($i = $fromInput; $i -le $toInput; $i++) {
$destinationDir = '\\pc' + '{0:d5}' -f #($i) + "\$shareName\$dupDir"
$netUseDir = '\\pc' + '{0:d5}' -f #($i) + "\$shareName"
$passObj = 'pass#' + '{0:d3}' -f #($i)
}
So it would loop through PC's from 1 to 99 but what I need now is to loop through a list of numbers that User inputs that are split
I am trying to do that with a foreach loop but it is not working for me like the one in the for loop:
$userInput = Read-Host "Input numbers divided by a comma [", "]"
$numberList = $userInput.split(", ")
foreach ($i in $numberList) {
$destinationDir = '\\pc' + '{0:d5}' -f #($i) + "\$shareName\$dupDir"
$netUseDir = '\\pc' + '{0:d5}' -f #($i) + "\$shareName"
$passObj = 'pass#' + '{0:d3}' -f #($i)
}
How do I make a foreach loop that takes the $userInput, splits it into $numberList and then loops for each of the numbers in the $numberList the way it is shown above.
I much appreciate your help as always!
The main issue is that you are applying formatting (d5) to a string that is intended for an integer type. You can simply cast to [int] to get the desired result.
foreach ($i in $numberList) {
$destinationDir = '\\pc' + '{0:d5}' -f [int]$i + "\$shareName\$dupDir"
$netUseDir = '\\pc' + '{0:d5}' -f [int]$i + "\$shareName"
$passObj = 'pass#' + '{0:d3}' -f [int]$i
}
Read-Host reads data as a [string]. If that data needs to be a different type for whatever reason, a conversion will be needed whether that be implicit or explicit.
First, for user input, I recommend you use something like this:
$userInput = Read-Host "Input numbers divided by a comma [", "]"
try
{
[int[]]$numberList = $userInput.split(',')
}
catch
{
'Input only numbers separated by commas.'
break
}
To explain why [int[]] is there and why a try {...} catch {...} statement:
We are attempting to convert a string into an array and convert the resulting elements into int. As a result we should be getting an array of integers, if this was not the case, meaning if the user inputs something different than numbers separated by commas, we would get an error which is captured and handled by the catch block.
In this case, converting the strings to integers is needed by showing you a simple example:
PS /> '{0:d5}' -f '1' # String formatting on a string
1
PS /> '{0:d5}' -f 1 # String formatting on an integer
00001
Now for looping, here are the 3 easy alternatives I see:
With for loop:
for($i=$numberList[0];$i -le $numberList.Count;$i++)
{
$destinationDir = "\\pc{0:d5}\$shareName\$dupDir" -f $i
$netUseDir = "\\pc{0:d5}\$shareName" -f $i
$passObj = 'pass#{0:d3}' -f $i
}
With foreach loop:
foreach($i in $numberList)
{
$destinationDir = "\\pc{0:d5}\$shareName\$dupDir" -f $i
$netUseDir = "\\pc{0:d5}\$shareName" -f $i
$passObj = 'pass#{0:d3}' -f $i
}
With ForEach-Object loop:
$numberList | ForEach-Object {
$destinationDir = "\\pc{0:d5}\$shareName\$dupDir" -f $_
$netUseDir = "\\pc{0:d5}\$shareName" -f $_
$passObj = 'pass#{0:d3}' -f $_
}

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

I would like to loop this array and Insert the Value Array by Array

$number = $args[0]
$myarray = &{$h = #{}; foreach ($a in 1..$number) { $h["array$a"] = ""; } return $h; }
Write-Output $myarray
echo ============================================
if ($myarray["array1"] -eq "") {
$myarray["array1"] = "C:\myscripts\Powershell_testing\FolderA\aa.txt"
Write-Output $myarray
}
elseif ($myarray["array2"] -eq "") {
$myarray["array2"] = "C:\myscripts\Powershell_testing\FolderB\bb.txt"
Write-Output $myarray
}
elseif ($myarray["array3"] -eq "") {
$myarray["array3"] = "C:\myscripts\Powershell_testing\FolderC\cc.txt"
Write-Output $myarray
}
else {
Write-Output "Array is file exist"
}
enter image description here
First of all, you are not dealing with arrays at all. Your code is apparently meant to fill a Hashtable with constructed file paths as values.
Why not use a simple for loop to construct this filepath and add to the hash at the same time?
Something like:
$number = 4
$myHash = #{}
for ($i = 1; $i -le $number; $i++) {
# construct a file path and add that as value to the hashtable
$file = "C:\myscripts\Powershell_testing\Folder{0}\{1}{1}.txt" -f [char](64 + $i), [char](96 + $i)
$myHash["value$i"] = $file
}
$myHash
Output:
Name Value
---- -----
value1 C:\myscripts\Powershell_testing\FolderA\aa.txt
value3 C:\myscripts\Powershell_testing\FolderC\cc.txt
value4 C:\myscripts\Powershell_testing\FolderD\dd.txt
value2 C:\myscripts\Powershell_testing\FolderB\bb.txt
Or if you want it ordered (which a Hashtable by definition is not):
$number = 4
$myHash = [ordered]#{}
for ($i = 1; $i -le $number; $i++) {
# construct a file path and add that as value to the hashtable
$file = "C:\myscripts\Powershell_testing\Folder{0}\{1}{1}.txt" -f [char](64 + $i), [char](96 + $i)
$myHash["value$i"] = $file
}
$myHash
Output:
Name Value
---- -----
value1 C:\myscripts\Powershell_testing\FolderA\aa.txt
value2 C:\myscripts\Powershell_testing\FolderB\bb.txt
value3 C:\myscripts\Powershell_testing\FolderC\cc.txt
value4 C:\myscripts\Powershell_testing\FolderD\dd.txt
[char](64 + $i) and [char](96 + $i) give you the characters you need. ASCII 65 --> A and ASCII 97 --> a, and so on.

Convert Array of Numbers into a String of Ranges

I was asking myself how easily you could convert an Array of Numbers Like = 1,2,3,6,7,8,9,12,13,15 into 1 String that "Minimizes" the numbers, so Like = "1-3,6-9,12-13,15".
I am probably overthinking it because right now I don't know how I could achieve this easily.
My Attempt:
$newArray = ""
$array = 1,2,3,6,7,8,9,12,13,15
$before
Foreach($num in $array){
If(($num-1) -eq $before){
# Here Im probably overthinking it because I don't know how I should continue
}else{
$before = $num
$newArray += $num
}
}
This should working, Code is self explaining, hopefully:
$array = #( 1,2,3,6,7,8,9,12,13,15 )
$result = "$($array[0])"
$last = $array[0]
for( $i = 1; $i -lt $array.Length; $i++ ) {
$current = $array[$i]
if( $current -eq $last + 1 ) {
if( !$result.EndsWith('-') ) {
$result += '-'
}
}
elseif( $result.EndsWith('-') ) {
$result += "$last,$current"
}
else {
$result += ",$current"
}
$last = $current
}
if( $result.EndsWith('-') ) {
$result += "$last"
}
$result = $result.Trim(',')
$result = '"' + $result.Replace(',', '","') +'"'
$result
I have a slightly different approach, but was a little too slow to answer. Here it is:
$newArray = ""
$array = 1,2,3,6,7,8,9,12,13,15
$i = 0
while($i -lt $array.Length)
{
$first = $array[$i]
$last = $array[$i]
# while the next number is the successor increment last
while ($array[$i]+1 -eq $array[$i+1] -and ($i -lt $array.Length))
{
$last = $array[++$i]
}
# if only one in the interval, output that
if ($first -eq $last)
{
$newArray += $first
}
else
{
# else output first and last
$newArray += "$first-$last"
}
# don't add the final comma
if ($i -ne $array.Length-1)
{
$newArray += ","
}
$i++
}
$newArray
Here is another approach to the problem. Firstly, you can group elements by index into a hashtable, using index - element as the key. Secondly, you need to sort the dictionary by key then collect the range strings split by "-" in an array. Finally, you can simply join this array by "," and output the result.
$array = 1, 2, 3, 6, 7, 8, 9, 12, 13, 15
$ranges = #{ }
for ($i = 0; $i -lt $array.Length; $i++) {
$key = $i - $array[$i]
if (-not ($ranges.ContainsKey($key))) {
$ranges[$key] = #()
}
$ranges[$key] += $array[$i]
}
$sequences = #()
$ranges.GetEnumerator() | Sort-Object -Property Key -Descending | ForEach-Object {
$sequence = $_.Value
$start = $sequence[0]
if ($sequence.Length -gt 1) {
$end = $sequence[-1]
$sequences += "$start-$end"
}
else {
$sequences += $start
}
}
Write-Output ($sequences -join ",")
Output:
1-3,6-9,12-13,15