Displaying user inputs with powershell - powershell

say I have an array
$something = #(
"first",
"second"
)
how can I display this to the user as
1. first
2. second
Selection :
I am able to do this by hash table and manually mapping
#{
1="first"
2="second"
};
and doing the following
$something.Keys | sort |% { Write-Host $_ ")" $something.Item($_) }
[int32]$constuctPayload.Action = Read-Host
but if need to perform this using an array how can I do this. I.e looping over the item and displaying with index for user selection. ?

You could use the IndexOf() method, to find the index in the array.
$something | ForEach-Object {Write-Host "$([Array]::IndexOf($something, $_)). $_ "}
Standard warning about being careful with Write-Host. Also you might want to look into Out-GridView.

Use a for loop to iterate over the elements of the array and prepend each value with the index + 1.
$something = 'first', 'second'
for ($i = 0; $i -lt $something.Count; $i++) {
Write-Host ('{0}. {1}' -f ($i+1), $something[$i])
}
[int32]$constuctPayload.Action = Read-Host -Prompt 'Selection'
I would recommend using the PromptForChoice() method over Read-Host, though:
$something = '&first', '&second'
$title = 'The title.'
$msg = 'Selection?'
$choices = $something | ForEach-Object {
New-Object Management.Automation.Host.ChoiceDescription $_
}
$options = [Management.Automation.Host.ChoiceDescription[]] $choices
$default = 0
$constuctPayload.Action = $Host.UI.PromptForChoice($title, $msg, $options, $default)

Related

Powershell: Arrays index acess and check if previous is substring to next

I need to link the job to the subjob: the job is of this format for example ACGN100Q while the subjobs that are attached are sequential and of this format: ACGN-100Q-000T;ACGN-100Q-010T;ACGN-100Q-020T;ACGN-100Q-030T
In my csv file the type of this job ACGN100Q is "TechnologyInteraction" while the subjobs are of type "TechnologyService". I am developing a script that allows me to say for example that the link between ACGN-100Q-000T and ACGN-100Q-010T is of type "TrigerringRelation" and the link between ACGN100Q and ACGN-100Q-000T is of type "RealizationRelation". I need help because I can't make the link.
Here is my starting csv file :
newElements.csv
ID,"Type","Name","Documentation"
eb214110-2b6a-48b2-ba5a-7c13dc3bba39,"TechnologyInteraction","ACGN100Q","Begin Of JobStream"
a46681e7-19a8-4fc5-b747-09679c15ff26,"TechnologyService","ACGN-100Q-000T","Transfert UDM (xACGN000)"
85761a09-1145-4037-a527-66a743def45f,"TechnologyService","ACGN-100Q-010T","move fichier REF to work"
27b126fb-c708-427d-b0a6-ce4747114ac4,"TechnologyService","ACGN-100Q-020T","w_read_account"
bb0c5e42-5fad-4bd9-8ee9-f41d0b824e82,"TechnologyService","ACGN-100Q-030T","w_read_referential"
0b8b76e3-62fa-4527-9f05-2eb4dbaa8e97,"TechnologyService","ACGN-100Q-040T","w_load_CompanyGroup"
1f487986-3cac-4af8-bda2-6400a1c71f48,"TechnologyService","ACGN-100Q-050T","w_load_Company"
And I want to get a file that looks like this:
relation.csv
"ID","Type","Name","Documentation","Source","Target"
"New ID","RealizationRelationship","","","eb214110-2b6a-48b2-ba5a-7c13dc3bba39","a46681e7-19a8-4fc5-b747-09679c15ff26"
"New ID","TriggeringRelationship","","","a46681e7-19a8-4fc5-b747-09679c15ff26","85761a09-1145-4037-a527-66a743def45f"
"New ID","TriggeringRelationship","","","85761a09-1145-4037-a527-66a743def45f","27b126fb-c708-427d-b0a6-ce4747114ac4"
"New ID","TriggeringRelationship","","","27b126fb-c708-427d-b0a6-ce4747114ac4","bb0c5e42-5fad-4bd9-8ee9-f41d0b824e82"
"New ID","TriggeringRelationship","","","bb0c5e42-5fad-4bd9-8ee9-f41d0b824e82","0b8b76e3-62fa-4527-9f05-2eb4dbaa8e97"
"New ID","TriggeringRelationship","","","0b8b76e3-62fa-4527-9f05-2eb4dbaa8e97","1f487986-3cac-4af8-bda2-6400a1c71f48"
$result= #()
function linkedRelationCsvToElementsCsv{
#relations.csv headers are ID,"Type","Name","Documentation","Source","Target"
$Type=#()
$Name=#()
$ID=#()
$Documentation=#()
#$pattern="^(WEBX|DWHS|COGN|CLOT|CLAI|BTRE|BISI|BDDO|ARXL|AGSO|AGPC|ACTO|FNET|ARX|AGS|INF|CLA|MEM|SWA|REX)-"
$newElementsCsv=Import-CSV $env:USERPROFILE\Desktop\Archi\newElements.csv |sort ID,"Type","Name","Documentation" -Unique
# Check if type is TechnologyInteraction or TechnologyService and link :TechnologyService to TechnologyInteraction and TechnologyInteraction to TWS id's
ForEach ($line in $newElementsCsv){
$Type += $line.Type
$Name += $line.Name
$ID +=$line.ID
$Documentation += $_.Documentation
#Search for element type in elements.csv
for( $index=0; $index -le $Name.length-1; $index++){
if($Type[$index] -eq 'TechnologyInteraction' -or $Type[$index] -eq 'TechnologyEvent' ){
Write-Warning "Case type TechnologyInteraction founded, new type of RealizationRelationship created "
# if the job is of type "TechnologyInteraction" or "TechnologyEvent", we link it to the TWS id's(TechnologyCollaboration,ef2f510b-924b-439d-8720-0183c7294eb3) in archi.
$newArrayResult= New-Object PsObject -Property #{ID=[guid]::NewGuid().ToString(); "Type"="RealizationRelationship"; "Name"=$Name[$index]; "Documentation"=$Documentation[$index]; "Source"="ef2f510b-924b-439d-8720-0183c7294eb3"; "Target"=$ID[$index]}
$result = $result + $newArrayResult
}elseif ($Type[$index][0].Type -eq 'TechnologyService' -and$Type[$index][1].Type -eq 'TechnologyService' ){
Write-Warning "Case type TechnologyService founded, new type of TriggeringRelationship created "
$newArrayResult2 = New-Object PsObject -Property #{ID=[guid]::NewGuid().ToString(); "Type"="TriggeringRelationship"; "Name"=""; "Documentation"=""; "Source"=$line[$index][0].ID; "Target"=$line[$index][1].ID}
$result = $result + $newArrayResult2
}
}
}
$result |Select-Object -Property ID,"Type","Name","Documentation","Source","Target"| Export-Csv $env:USERPROFILE\Desktop\Archi\relation.csv -NoTypeInformation -Encoding UTF8
}linkedRelationCsvToElementsCsv # Call the function
> the elseIf() not return value.
Thanks you in advance.
The following code generates exactly the output you want for exactly the input you've given. There might be unexpected edge cases, so you should write some tests (e.g. with Pester) to confirm it behaves how you want it to in those edge cases.
The key is that the output for any row includes the ID of the previous row as well as the current row, so we keep the previous row in a variable during the foreach loop so we can inspect it when we process the next row, and the Type in the output just depends on the Type of the previous row.
Note that I've also moved the conversion to / from csv out of the main function so it's easier to unit test the function in isolation.
function ConvertTo-ElementItems
{
param
(
[object[]] $Relations
)
$jobTypes = #( "TechnologyInteraction", "TechnologyEvent" );
$subTypes = #( "TechnologyService" );
$previousItem = $null;
foreach( $item in $Relations )
{
if( $item.Type -in $jobTypes )
{
# start of a new job, but don't output anything
}
elseif( $item.Type -notin $subTypes )
{
# not a subjob type that we recognise
throw "unrecognised subjob type '$($item.Type)' for subjob '$($item.ID)'";
}
elseif( $null -eq $previousItem )
{
# we've got a subjob, but there was no previous job or subjob
throw "no preceding item for subjob '$($item.ID)'";
}
elseif( $previousItem.Type -in $jobTypes )
{
# this is the first subjob after the parent job
new-object PSCustomObject -Property ([ordered] #{
"ID" = "New ID"
"Type" = "RealizationRelationship"
"Name" = "";
"Documentation" = ""
"Source" = $previousItem.ID
"Target" = $item.ID
});
}
else
{
# the preceding item was a subjob as well
new-object PSCustomObject -Property ([ordered] #{
"ID" = "New ID"
"Type" = "TriggeringRelationship"
"Name" = ""
"Documentation" = ""
"Source" = $previousItem.ID
"Target" = $item.ID
});
}
$previousItem = $item;
}
}
And here's an example of using the function:
$ErrorActionPreference = "Stop";
Set-StrictMode -Version "Latest";
$inputCsv = #"
ID,"Type","Name","Documentation"
eb214110-2b6a-48b2-ba5a-7c13dc3bba39,"TechnologyInteraction","ACGN100Q","Begin Of JobStream"
a46681e7-19a8-4fc5-b747-09679c15ff26,"TechnologyService","ACGN-100Q-000T","Transfert UDM (xACGN000)"
85761a09-1145-4037-a527-66a743def45f,"TechnologyService","ACGN-100Q-010T","move fichier REF to work"
27b126fb-c708-427d-b0a6-ce4747114ac4,"TechnologyService","ACGN-100Q-020T","w_read_account"
bb0c5e42-5fad-4bd9-8ee9-f41d0b824e82,"TechnologyService","ACGN-100Q-030T","w_read_referential"
0b8b76e3-62fa-4527-9f05-2eb4dbaa8e97,"TechnologyService","ACGN-100Q-040T","w_load_CompanyGroup"
1f487986-3cac-4af8-bda2-6400a1c71f48,"TechnologyService","ACGN-100Q-050T","w_load_Company"
"#
$expectedCsv = #"
"ID","Type","Name","Documentation","Source","Target"
"New ID","RealizationRelationship","","","eb214110-2b6a-48b2-ba5a-7c13dc3bba39","a46681e7-19a8-4fc5-b747-09679c15ff26"
"New ID","TriggeringRelationship","","","a46681e7-19a8-4fc5-b747-09679c15ff26","85761a09-1145-4037-a527-66a743def45f"
"New ID","TriggeringRelationship","","","85761a09-1145-4037-a527-66a743def45f","27b126fb-c708-427d-b0a6-ce4747114ac4"
"New ID","TriggeringRelationship","","","27b126fb-c708-427d-b0a6-ce4747114ac4","bb0c5e42-5fad-4bd9-8ee9-f41d0b824e82"
"New ID","TriggeringRelationship","","","bb0c5e42-5fad-4bd9-8ee9-f41d0b824e82","0b8b76e3-62fa-4527-9f05-2eb4dbaa8e97"
"New ID","TriggeringRelationship","","","0b8b76e3-62fa-4527-9f05-2eb4dbaa8e97","1f487986-3cac-4af8-bda2-6400a1c71f48"
"#;
$relations = $inputCsv | ConvertFrom-Csv;
$elements = ConvertTo-ElementItems -Relations $relations;
$outputCsv = ($elements | ConvertTo-Csv -NoTypeInformation) -join "`n";
if( $outputCsv -ne $expectedCsv )
{
throw "output csv doesn't match expected csv";
} else {
write-host "output csv matches expected csv";
}

powershell: if loop, while the first position of arraylist in in process

I have an arraylist, in which I am going to save some values. I am doing a foreach loop and if a value is going to be saved at the first position of the ArrayList, I want to output a "First position" line and otherwise, nothing should be done.
The if block that I wrote below, doesn't work.
<# Don't consider this code from here
[System.Collections.ArrayList]$alist = #()
$al ="keeranpc01,www.google.ch,192.168.0.25"
$al = $al.Split(",")
foreach($h in $al){
# Abstand vor dem Hostnamen löschen
if($h.contains(' ')) {
$h = $h -replace '\s', ''
}
$alist.Add($h)
}
to here
#>
#### Start here
[System.Collections.ArrayList]$rear = #()
foreach($ha in $alist) {
$PingConnetion = Test-Connection $ha -Count 1 -ErrorAction SilentlyContinue
$pcre = $ha
$pire = if($PingConnetion.ResponseTime -ne $null) {Write-output 'Erreichbar'}else{Write-Output 'Nicht Erreichbar'}
$zure = $PingConnetion.ResponseTime
$zeit = Get-Date -Format HH:mm:ss
if($alist[$_] -eq $alist[0]) {Write-Host 'First position'}
[void]$rear.Add([PSCustomObject]#{Zeit = $zeit; Host = $pcre; IPv4 = $PingConnetion.IPV4Address.IPAddressToString; Ping = $pire; Zugriffszeit = $zure; })
}
how should I write the if statement so that it is possible? I expect if-statement to work, Only when the first position of ArrayList is in process
thx
What you are tying to do does work except you are tying to compare element zero of your alist to a ordinal position of your alist which is invalid. You would need to compare the following:
if($ha -eq $alist[0]) {Write-Host 'First position'}
Below is a worked example that might be clearer.
$input = 1..10
foreach($x in $input){
if($input[0] -eq $x){
write-host "First Position"
}
$x
}

Powershell - combine switch statement and loop

I have a piece of code presented below, it takes values from a json file. This is a array of string --> $Json.Names. I would like to avoid duplicate lines like --> $Json.Names[0].Name {$Json.Names[0].Name; break}. Second case is that Array $Json.Names could have different length, array can have 6 and more or less elements. I want to make this switch statement more elastic. I tried to use for loop and while loop, but in this case these loops doesn't help me. Is there any clever method to make this code more sophisticated/elastic and avoid duplicate mentioned code lines$Json.Names[0].Name {$Json.Names[0].Name; break}
$Json = Get-Content "$path" | out-string | ConvertFrom-Json
$Name = switch ($Member) {
$Json.Names[0].Name {$Json.Names[0].Name; break}
$Json.Names[1].Name {$Json.Names[1].Name; break}
$Json.Names[2].Name {$Json.Names[2].Name; break}
$Json.Names[3].Name {$Json.Names[3].Name; break}
$Json.Names[4].Name {$Json.Names[4].Name; break}
$Json.Names[5].Name {$Json.Names[5].Name; break}
$Json.Names[6].Name {$Json.Names[6].Name; break}
default {"Unknown Name"}
}
Assuming this structure:
$json = [pscustomobject]#{names = [pscustomobject]#{name ='joe'},
[pscustomobject]#{name ='john'},
[pscustomobject]#{name ='james'}}
Assuming $member is a single name, you can say
$name = $json.names.name -eq $member # an array of one
$name would be a null array if there's no match.
if (! $name) { $name = 'Unknown Name' }
Or, in the language of Powershell 7 preview 5:
$name ??= 'Unknown Name'
You also may want to make a hashtable of the names.
You can take advantage of property enumeration and simplify your code pretty significantly:
# define our default
$Name = "Unknown Name"
# define the list of names
$Json = Get-Content "$path" | ConvertFrom-Json
$Names = $Json.Names.Name
# update $Name if applicable
if($Names -contains $member){
$Name = $member
}

How to create ForEach loop to go through multiple variables Powershell

I've got a problem with ForEach loop. Im trying to loop through multiple variables of same kind just increment different.
Im trying to change the TextBox Text depending on if Label from same row has text.
This is how I could make it to just write and IF sentence for each Label but I was looking a way to loop each of these blocks through ForEach loop. I've got total of 8 Labels and Textboxes.
Here is the code: ( Im sure you'll figure out what I'm after :) )
IF ( $Label1.Text.Length -ne 0 )
{
$Label1.Visible = $true
$TextBox1.Visible = $true
$TextBox1.Text = ( "Enter new name for " + $Label1.Text )
}
example of ForEach
$Count = 1..8
$Count | ForEach-Object {
IF ( $Label($_).Text.Length -ne 0 )
{
$Label($_).Visible = $true
$TextBox($_).Visible = $true
$TextBox($_).Text = ( "Enter new name for " + $Label($_).Text )
}
}
etc...
I tried putting variables in array and loop through that way but ofcourse array changes the type to string and it doesnt work...
Give this a try, I can't test it using label & textbox object but it can work tuning it better:
1..8 | ForEach-Object {
IF ( (iex "`$Label$_.Text.Length") -ne 0 )
{
iex "`$Label$_.Visible = `$true"
iex "`$TextBox$_.Visible = `$true"
iex "`$TextBox$_.Text = 'Enter new name for ' + `$Label$_.Text"
}
}
You can use the Get-Variable cmdlet for that purpose:
1..8 | ForEach-Object {
if ( (Get-Variable "Label$_").Value.Text.Length -ne 0 )
{
(Get-Variable "Label$_").Value.Visible = $true
(Get-Variable "Label$_").Value.Visible = $true
(Get-Variable "Label$_").Value.Text = ( "Enter new name for " + (Get-Variable "Label$_").Value.Text )
}
}

How can I alternate/switch parts of a string in PowerShell without using an intermediate value?

I'm trying to alternate a setting in a config file using PowerShell. For example, if a certain value is true, I'd like to switch it to false. If it's false, I'd like to switch it to true. I'd also like to change a path from \\servername\folder\ to \\servername\. Is there a way I can perform this in PowerShell without using an intermediate value?
If I do this:
$foo = 'aaa'
$foo -replace 'aaa', 'bbb' -replace 'bbb', 'aaa'
$foo will always be 'aaa'. I realize I could make an intermittent change ('aaa' becomes 'ccc' and then changes to 'bbb') but that's messy to read.
How can I alternate values without using an intermediate value?
I'm not quite sure what you're trying to achieve. Do you want to switch two values without a buffer variable? Or do you want to toggle a value between two states? The latter can be achieved like this:
function Toggle($s, $v1, $v2) {
$e1 = [regex]::Escape($v1)
$e2 = [regex]::Escape($v2)
$r = $s
if ($s -match $e1) {
$r = $s -replace $e1, $v2
} elseif ($s -match $e2) {
$r = $s -replace $e2, $v1
}
return ($r)
}
$foo = "..."
Toggle $foo "\\servername\folder\" "\\servername\"
Using a match evaluator. Not sure if this is any better or not.
Begin{
[regex]$ValueRegex = 'aaa|bbb'
$ValueToggles = #{
aaa='bbb'
bbb='aaa'
}
$toggleValue = {$ValueToggles[$args[0].groups[0].value]}
}
Process{
$foo = 'Value: aaa'
$ValueRegex.replace($foo,$toggleValue)
}
Value: bbb
You can alternate a boolean value ($true or $false) simply by using the -not operator; e.g.:
PS C:\> $value = $true
PS C:\> $value = -not $value
PS C:\> $value
False
Regular expression replacement:
PS C:\> '\\servername\folder\' -replace '^(\\\\[^\\]+)\\[^\\]+', '$1'
\\servername\
Bill