Remove the last character of an array - powershell

i'm trying to remove the last character of an array.
I have an array with:
name1;
name2;
Now i want to remove the last ";".
I'v though on something like:
$array = $array | Select -last 1 | foreach{$_.replace(";", "")}
But thats just an idea. Also not working, because it will leave only the last entry.

To change just the last object, process every object of the array and check if it's the last. replace only on the last one:
$array = $array | ForEach-Object {
if( $array.IndexOf($_) -eq ($array.count -1) ){
$_.replace(";","")
}else{$_}
}

Try this to remove/replace last semi colon ';' from each element.
$array | foreach{ $_ -replace ';$', '' }
and if you want to remove the same from any where in the string, just remove the $

Related

How to make netstat output's headings show properly in out-gridview?

when I use:
netstat -f | out-gridview
in PowerShell 7.3, I get the window but it has only one column which is a string. I don't know why it's not properly creating a column for each of the headings like Proto, Local Address etc.
how can I fix this?
While commenter Toni makes a good point to use Get-NetTCPConnection | Out-GridView instead, this answer addresses the question as asked.
To be able to show output of netstat in grid view, we have to parse its textual output into objects.
Fortunately, all fields are separated by at least two space characters, so after replacing these with comma, we can simply use ConvertFrom-CSV (thanks to an idea of commenter Doug Maurer).
netstat -f |
# Skip unwanted lines at the beginning
Select-Object -skip 3 |
# Replace two or more white space characters by comma, except at start of line
ForEach-Object { $_ -replace '(?<!^)\s{2,}', ',' } |
# Convert into an object and add it to grid view
ConvertFrom-Csv | Out-GridView
For a detailed explanation of the RegEx pattern used with the -replace operator, see this RegEx101 demo page.
This is the code of my original answer, which is functionally equivalent. I'll keep it as an example of how choosing the right tool for the job can greatly simplify code.
$headers = #()
# Skip first 3 lines of output which we don't need
netstat -f | Select-Object -skip 3 | ForEach-Object {
# Split each line into columns
$columns = $_.Trim() -split '\s{2,}'
if( -not $headers ) {
# First line is the header row
$headers = $columns
}
else {
# Create an ordered hashtable
$objectProperties = [ordered] #{}
$i = 0
# Loop over the columns and use the header columns as property names
foreach( $key in $headers ) {
$objectProperties[ $key ] = $columns[ $i++ ]
}
# Convert the hashtable into an object that can be shown by Out-GridView
[PSCustomObject] $objectProperties
}
} | Out-GridView

Powershell: Find any value in a CSV column and replace it with a single value

I have a CSV file where I have to find any non-blank value in 2 specific columns and replace them with 'Yes'
My data looks like this where it can have either both blank, value in either column, or in both.
Letter Grade
Numeric Grade
A
10
C
5
I want it to look like this when I'm done
Letter Grade
Numeric Grade
Yes
Yes
Yes
Yes
I have 2 problems, addressing columns that have a space in the name (tried wrapping with " and ' and {) and regex to match any non-empty value. It works with the code below to simply replace a and if the column is Letter instead of Letter Grade.
I tried .+ to match anything in the cell, but I get no matches.
Thanks in advance!
Import-Csv -Path ".\test.csv"| ForEach-Object {
if ($_.Letter -eq 'a') {
$_.Letter = 'Yes'
}
$_
} | Export-Csv .\poop2.csv -Encoding UTF8
You could handle this programmatically by, first, collecting all property names from the first object (done via accessing of intrinsic member PSObject in this example) and then enumerating each property of each object coming from the pipeline and checking if it matches \S (any non-whitespace character).
Import-Csv path\to\csv.csv | ForEach-Object { $isFirstObject = $true } {
if($isFirstObject) {
$properties = $_.PSObject.Properties.Name
$isFirstObject = $false
}
foreach($property in $properties) {
if($_.$property -match '\S') {
$_.$property = 'Yes'
}
}
$_
} | Export-Csv path\to\newcsv.csv -NoTypeInformation
If, instead of programmatically gathering the object's property names, you wanted to use specific / hardcoded properties, the code would be simpler:
$properties = 'Letter Grade', 'Numeric Grade'
Import-Csv path\to\csv.csv | ForEach-Object {
foreach($property in $properties) {
if($_.$property -match '\S') {
$_.$property = 'Yes'
}
}
$_
} | Export-Csv path\to\newcsv.csv -NoTypeInformation

PowerShell - Convert Property Names from Pascal Case to Upper Case With Underscores

Let's say I have an object like this:
$test = #{
ThisIsTheFirstColumn = "ValueInFirstColumn";
ThisIsTheSecondColumn = "ValueInSecondColumn"
}
and I want to end up with:
$test = #{
THIS_IS_THE_FIRST_COLUMN = "ValueInFirstColumn";
THIS_IS_THE_SECOND_COLUMN = "ValueInSecondColumn"
}
without manually coding the new column names.
This shows me the values I want:
$test.PsObject.Properties | where-object { $_.Name -eq "Keys" } | select -expand value | foreach{ ($_.substring(0,1).toupper() + $_.substring(1) -creplace '[^\p{Ll}\s]', '_$&').Trim("_").ToUpper()} | Out-Host
which results in:
THIS_IS_THE_FIRST_COLUMN
THIS_IS_THE_SECOND_COLUMN
but now I can't seem to figure out how to assign these new values back to the object.
You can modify hashtable $test in place as follows:
foreach($key in #($test.Keys)) { # !! #(...) is required - see below.
$value = $test[$key] # save value
$test.Remove($key) # remove old entry
# Recreate the entry with the transformed name.
$test[($key -creplace '(?<!^)\p{Lu}', '_$&').ToUpper()] = $value
}
#($test.Keys) creates an array from the existing hashtable keys; #(...) ensures that the key collection is copied to a static array, because using the .Keys property directly in a loop that modifies the same hashtable would break.
The loop body saves the value for the input key at hand and then removes the entry under its old name.[1]
The entry is then recreated under its new key name using the desired name transformation:
$key -creplace '(?<!^)\p{Lu} matches every uppercase letter (\p{Lu}) in a given key, except at the start of the string ((?<!^)), and replaces it with _ followed by that letter (_$&); converting the result to uppercase (.ToUpper()) yields the desired name.
[1] Removing the old entry before adding the renamed one avoids problems with single-word names such as Simplest, whose transformed name, SIMPLEST, is considered the same name due to the case-insensitivity of hasthables in PowerShell. Thus, assigning a value to entry SIMPLEST while entry Simplest still exists actually targets the existing entry, and the subsequent $test.Remove($key) would then simply remove that entry, without having added a new one.
Tip of the hat to JosefZ for pointing out the problem.
I wonder if it is possible to do it in place on the original object?
($test.PsObject.Properties|Where-Object {$_.Name -eq "Keys"}).IsSettable says False. Hence, you need do it in two steps as follows:
$test = #{
ThisIsTheFirstColumn = "ValueInFirstColumn";
ThisIsTheSecondColumn = "ValueInSecondColumn"
}
$auxarr = $test.PsObject.Properties |
Where-Object { $_.Name -eq "Keys" } |
select -ExpandProperty value
$auxarr | ForEach-Object {
$aux = ($_.substring(0,1).toupper() +
$_.substring(1) -creplace '[^\p{Ll}\s]', '_$&').Trim("_").ToUpper()
$test.ADD( $aux, $test.$_)
$test.Remove( $_)
}
$test
Two-step approach is necessary as an attempt to perform REMOVE and ADD methods in the only pipeline leads to the following error:
select : Collection was modified; enumeration operation may not execute.
Edit. Unfortunately, the above solution would fail in case of an one-word Pascal Case key, e.g. for Simplest = "ValueInSimplest". Here's the improved script:
$test = #{
ThisIsTheFirstColumn = "ValueInFirstColumn";
ThisIsTheSecondColumn = "ValueInSecondColumn"
Simplest = "ValueInSimplest" # the simplest (one word) PascalCase
}
$auxarr = $test.PsObject.Properties |
Where-Object { $_.Name -eq "Keys" } |
select -ExpandProperty value
$auxarr | ForEach-Object {
$aux = ($_.substring(0,1).toupper() +
$_.substring(1) -creplace '[^\p{Ll}\s]', '_$&').Trim("_").ToUpper()
$newvalue = $test.$_
$test.Remove( $_)
$test.Add( $aux, $newvalue)
}
$test
This seems to work. I ended up putting stuff in a new hashtable, though.
$test = #{
ThisIsTheFirstColumn = "ValueInFirstColumn";
ThisIsTheSecondColumn = "ValueInSecondColumn"
}
$test2=#{}
$test.PsObject.Properties |
where-object { $_.Name -eq "Keys" } |
select -expand value | foreach{ $originalPropertyName=$_
$prop=($_.substring(0,1).toupper() + $_.substring(1) -creplace '[^\p{Ll}\s]', '_$&').Trim("_").ToUpper()
$test2.Add($prop,$test[$originalPropertyName])
}
$test2

How to remove characters from an item in an array using PowerShell

I have an array of computer names, $computers, that are in a FQDN format. I want to trim all characters to the right of the first period and including the period.
Ex: server-01.mydomain.int = server-01
This is what I tried but it errors out.
$computers = Get-VM | Select Name
$computers = $computers.Substring(0, $computers.IndexOf('.'))
$computers
When you do |Select Name, PowerShell returns an object with a Name property, rather than only the value of each Name property of the input objects.
You could change it to Select -ExpandProperty Name and then iterate over each item in the array for the actual substring operation using a loop or ForEach-Object - although we can skip the first step completely:
$computers = Get-VM |ForEach-Object { $_.Name.Substring(0, $_.Name.Indexof('.')) }
Or another way.
$computers = Get-VM | ForEach-Object { ($_.Name -split ".")[0] }
Since you are always selecting the first string before the first "." you can just split at the dot and select the first element in the resulting array.

powershell script delete first character in output

How would I delete the first line in the output from this command? If the output is A123456, I just want it to show me 123456 with the A.
Get-User $_ | Select sAMAccountName
Just get the substring starting at the second letter(index 1).
Get-User $_ | Select #{n="AccountName";e={$_.sAMAccountName.Substring(1)}}
If you just need the value, you could do it like this:
Get-User $_ | % { $_.sAMAccountName.Substring(1) }
Substring(1) returns a substring containing all chars after the first one.
Get-User $_ | Select #{N="myAccountName";E={$_.sAMAccountName).substring(1)}}
You can remove the first character with the -replace operator:
(Get-User $_ | select -Expand sAMAccountName) -replace '^.'
Or
Get-User $_ | select #{n='sAMAccountName';e={$_.sAMAccountName -replace '^.'}}
if you want to keep objects rather than strings.
$str = "#123456";
$str = $str.Substring(1,($str.Length-1));
$str;