How to concatenate array of integers into comma separated string - powershell

I have two question, acually:
How to join array of integers into comma separated string?
(1,2,3) => "1,2,3"
How to convert array of integers to array of string? (1,2,3) => ("1", "2", "3")
$arraylist = New-Object 'system.collections.arraylist'
$arraylist.Add(1);
$arraylist.Add(2);
$csv = ??
#($arraylist-join -',') returns error: Cannot convert value "," to type "System.Int32". Error: "Input string was not in a correct format."

In your question, you've commented out the following snippet:
($arraylist-join -',')
because it returns the error Cannot convert value "," to type "System.Int32"...
The reason for this is the dash - in front of ','.
In PowerShell, only operators and parameters are prefixed with a dash, and since ',' is neither (it's an argument to an operator), the PowerShell parser gets super confused and tries to treat -',' as a value expression that would result in a negative number.
Just void the dash and you'll be fine:
$arraylist -join ','
Finally, you can easily cast an array of integers to an array of strings with the unchecked cast operator -as (PowerShell 3.0 and newer):
$StringArray = 1,2,3,4,5 -as [string[]]
or with an explicit cast (PowerShell 2.0-compatible):
$StringArray = [string[]]#(1,2,3,4,5)

Next code snippet could help out on understanding:
$arraylist = New-Object 'system.collections.arraylist'
$arraylist.Add(111) | Out-Null
$arraylist.Add([string]222) | Out-Null
$arraylist.Add('"' + 3 + '"') | Out-Null
for($i=0; $i -lt $arraylist.Count; $i++ ){
write-host $i, $arraylist[$i], $arraylist[$i].GetType()
}
write-host ''
$csv = $arraylist -join ','
$csv
Output:
0 111 System.Int32
1 222 System.String
2 "3" System.String
111,222,"3"
Additional view of (un)importance of " double quotes in a string type shows next + operation (sum of integers but concatenation of strings):
write-host $i, $arraylist[$i], $arraylist[$i].GetType().Name, ($arraylist[$i] + 55)
gives next output:
0 111 Int32 166
1 222 String 22255
2 "3" String "3"55
111,222,"3"
However, " double quotes have another important and meaningful role in .csv file when imported e.g. to Excel sheet.

This worked for me:
[String]::Join(",", $arraylist.ToArray())
I got 1,2.
And then the second part:
foreach($number in $arraylist) { $number.ToString() }

I don't if this is correct. Usually, they have powershell2. Just give this a little try.
$a = #()
$strArrayNum=""
for($i=0; $i -lt $arraylist.length; $i++ ){
$strArrayNum += $element
$strArrayNum = $i+1 -eq $arraylist.length ? "" : ","
}
$a = $strArrayNum.Split(",")

Related

PowerShell read column value from csv file [duplicate]

This question already has answers here:
How can you use an object's property in a double-quoted string?
(5 answers)
Closed 1 year ago.
This is my below input data which is in CSV format and roles,admin, accountant and security are columns.
roles, admin,accountant,security
Engineer, ,x , ,
I want to get value of rows using columns with below code, example , foreach for accountant column should return 'x', but I am getting something else.
$path = "$PSScriptRoot\Test.csv"
$csv = Import-Csv -path $path -Delimiter ","
$columns = $csv | Get-member -MemberType 'NoteProperty' | Select-Object -ExpandProperty 'Name'
$csv | ForEach-Object {
$row = $_
foreach ($col in $columns) {
Write-Host " vaalue of scripting is : $col "
Write-Host " vaalue of scripting row is : $row.$col "
Start-Sleep -s 10
}
}
Output I get is
vaalue of scripting is : accountant
vaalue of scripting row is : #{roles=Engineer; admin=; accountant=x ; security=}.accountant
vaalue of scripting is : admin
vaalue of scripting row is : #{roles=Engineer; admin=; accountant=x ; security=}.admin
vaalue of scripting is : roles
vaalue of scripting row is : #{roles=Engineer; admin=; accountant=x ; security=}.roles
How can I get 'x' for accountant column or any other column value using
Following from my comment, short answer was to use the Subexpression operator $( ), which would allow PowerShell to reference the property $col of the object $row.
Short extract from MS Docs:
Use this when you want to use an expression within another expression. For example, to embed the results of command in a string expression.
To give you a short explanation of why this is needed, using this as an example:
$object = [pscustomobject]#{
foo = 'var'
}
$property = 'foo'
When we do "$object.$property" or in simple terms, "$object.foo", the double quotes "..." are not allowing PowerShell to reference the foo property from $object because the dot . is interpreted as literal and not as a dot method. In addition, the quotes are converting $object to its stringified ? representation #{foo=var} followed by the literal dot . followed by the variable expansion of $property.
Another extract from about_Properties:
The most common way to get the values of the properties of an object is to use the dot method. Type a reference to the object, such as a variable that contains the object, or a command that gets the object. Then, type a dot (.) followed by the property name.
Lastly, what other alternatives do we have to get around this besides $(...):
String Formatting and String.Format method:
'Value of $object.$property is "{0}".' -f $object.$property
[string]::Format('Value of $object.$property is "{0}".', $object.$property)
Using + to concatenate strings is also a very known one:
'Value of $object.$property is "' + $object.$property + '".'
As a side note, and unrelated to the actual issue, this might be a more direct way of approaching your code:
#'
roles,admin,accountant,security
Engineer,,x,,
Operator,,y,,
'# |
ConvertFrom-Csv | ForEach-Object -Begin { $i = 1 } -Process {
foreach($Property in $_.PSObject.Properties.Name)
{
'Value of Row {0} Column "{1}" is "{2}"' -f
$i, $Property, (
'NULL', ($val = $_.$Property)
)[[int][bool]$val]
}
$i++
}
Note the use of .PSObject to access the object's properties and methods, an alternative to Get-Member.
The above would result in:
Value of Row 1 Column "roles" is "Engineer"
Value of Row 1 Column "admin" is "NULL"
Value of Row 1 Column "accountant" is "x"
Value of Row 1 Column "security" is "NULL"
Value of Row 2 Column "roles" is "Operator"
Value of Row 2 Column "admin" is "NULL"
Value of Row 2 Column "accountant" is "y"
Value of Row 2 Column "security" is "NULL"
I wrote this based on your csv file, let me know if it worked (change the folder path and file name).
$folderspath = 'C:\Test'
$csvfilename = 'info.csv'
$csvfilepath = $folderspath + "\" + $csvfilename
$csvfilepath = $csvfilepath.ToString()
$csvfile = Import-CSV -Path $csvfilepath -Delimiter ","
ForEach ($row in $csvfile) {
IF($row.security -eq "High") {
$Roles = $row.roles
$Admin = $row."admin"
$Accountant = $row.accountant
$Security = $row."security"
Write-Host "Roles: " $Roles "; Admin:" $Admin "; Accountant:" $Accountant "; `
Security:" $Security
}
}
The csv file I used
roles, admin,accountant,security
Engineer, ,x , ,
Engineer2,Yes ,x ,High,
Engineer3, No, , Low,

Get string length that is created dynamically

Lets say I want to do something like this:
$string = 'arbitrary string' # << is random
(Do-Something -Name ($string + (Get-Random)).Substring(0, [math]::Min(20, ??)
How do I refer to current length of the result of the expression inside ($string + (Get-Random))?
Fails when using Substring(0, 20) when string shorter than 20 chars
Exception calling "Substring" with "2" argument(s): "Index and length
must refer to a location within the string. Parameter name: length"
You'll have to assign the new string to a variable:
Do-Something -Name ($s = $string + (Get-Random)).Substring(0, [math]::Min(20,$s.Length))
A less terse version of the above:
$Name = $string + (Get-Random)
if($Name.Length -gt 20){
$Name = $Name.Substring(0, 20)
}
Do-Something -Name $Name
As mentioned in the comments, you could also select the first 20 chars by index from the string and combine again with the -join operator (v3.0 and newer):
$Name = "$string$(Get-Random)"[0..19] -join ''
Feeling frisky? Use regex (as suggested by wOxxOm):
$Name = "$string$(Get-Random)" -replace '^(.{20}).+','$1'
If the concatenated string is less than 20 characters nothing will be replaced, otherwise the entire string will match and get replaced by the first 20 characters
Another approach would be to generate a random number of X digits where X is 20 - $string.Length (only works if $string is guaranteed to be between 2 and 19 characters long):
$Name = "$string$(Get-Random -Min ('1' * (20 - $string.Length)) -Max ('9' * (20 - $string.Length)))"

Is there a better way to convert all control characters to entities in PowerShell 5?

Context: Azure, Windows Server 2012, PowerShell 5
I've got the following code to convert all control characters (ascii and unicode whitespace other than \x20 itself) to their ampersand-hash equivalents.
function ConvertTo-AmpersandHash {
param ([Parameter(Mandatory)][String]$Value)
# there's got to be a better way of doing this.
$AMPERHASH = '&#'
$SEMICOLON = ';'
for ($i = 0x0; $i -lt 0x20; $i++) {
$value = $value -replace [char]$i,($AMPERHASH + $i + $SEMICOLON)
}
for ($i = 0x7f; $i -le 0xa0; $i++) {
$value = $value -replace [char]$i,($AMPERHASH + $i + $SEMICOLON)
}
return $Value
}
As can be seen by the embedded comment, I'm sure there's a better way to do this. As it stands, one does some 65 iterations for each incoming string. Would regular expressions work better/faster?
LATER
-replace '([\x00-\x1f\x7f-\xa0])',('&#' + [byte][char]$1 + ';')
looks promising but the $1 is evaluating to zero all the time, giving me  all the time.
LATER STILL
Thinking that -replace couldn't internally iterate, I came up with
$t = [char]0 + [char]1 + [char]2 + [char]3 + [char]4 + [char]5 + [char]6
$r = '([\x00-\x1f\x7f-\xa0])'
while ($t -match [regex]$r) {
$t = $t -replace [regex]$r, ('&#' + [byte][char]$1 + ';')
}
echo $t
However out of that I still get

FINALLY
function ConvertTo-AmpersandHash {
param ([Parameter(Mandatory)][String]$Value)
$AMPERHASH = '&#'
$SEMICOLON = ';'
$patt = '([\x00-\x1f\x7f-\xa0]{1})'
while ($Value -match [regex]$patt) {
$Value = $Value -replace $Matches[0], ($AMPERHASH + [byte][char]$Matches[0] + $SEMICOLON)
}
return $Value
}
That works better. Faster too. Any advances on that?
Kory Gill's answer with the library call is surely a better approach, but to address your regex question, you can't evaluate code in the replacement with the -replace operator.
To do that, you need to use the .Net regex replace method, and pass it a scriptblock to evaluate the replacement, which takes a parameter of the match. e.g.
PS C:\> [regex]::Replace([string][char]2,
'([\x00-\x20\x7f-\xa0])',
{param([string]$m) '&#' + [byte][char]$m + ';'})

Your question is a little unclear to me, and could be a duplicate of What is the best way to escape HTML-specific characters in a string (PowerShell)?.
It would be nice if you explicitly stated the exact string you have and what you want it to converted to. One has to read the code to try to guess.
I am guessing one or more of these functions will do what you want:
$a = "http://foo.org/bar?baz & also <value> conversion"
"a"
$a
$b = [uri]::EscapeDataString($a)
"b"
$b
$c = [uri]::UnescapeDataString($b)
"c"
$c
Add-Type -AssemblyName System.Web
$d = [System.Web.HttpUtility]::HtmlEncode($a)
"d"
$d
$e = [System.Web.HttpUtility]::HtmlDecode($d)
"e"
$e
Gives:
a
http://foo.org/bar?baz & also <value> conversion
b
http%3A%2F%2Ffoo.org%2Fbar%3Fbaz%20%26%20also%20%3Cvalue%3E%20conversion
c
http://foo.org/bar?baz & also <value> conversion
d
http://foo.org/bar?baz & also <value> conversion
e
http://foo.org/bar?baz & also <value> conversion
I have one small function which helps me replacing as per my requirement:
$SpecChars are all the characters that are going to be replaced with nothing
Function Convert-ToFriendlyName
{param ($Text)
# Unwanted characters (includes spaces and '-') converted to a regex:
$SpecChars = '\', ' ','\\'
$remspecchars = [string]::join('|', ($SpecChars | % {[regex]::escape($_)}))
# Convert the text given to correct naming format (Uppercase)
$name = (Get-Culture).textinfo.totitlecase(“$Text”.tolower())
# Remove unwanted characters
$name = $name -replace $remspecchars, ""
$name
}
Example: Convert-ToFriendlyName "My\Name\isRana\Dip " will result me "MyNameIsranaDip".
Hope it helps you.

Powershell fixed width export

I am having a text file wich uses fixed width for separating columns.
I'm loading the file and create a new column which concatinates the values of the first two columns.
The problem I have that when exporting the data I need to define a fixed column width of 13 for Column C.
Column A (3) Column B(9) Column C(13)
MMA 12345 12345_MMA
MMO 987222 987222_MMO
Basically for this example in the export I am missing 4 spaces for the first row and 3 for the second row.
Thisis my current code, which also includes a new row for MD5 creation.
# Load input data
$PreSystem = [IO.File]::ReadAllText("C:\FILE.txt")
# Initiate md5-hashing
$md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
$utf8 = new-object -TypeName System.Text.UTF8Encoding
# Split input data by lines
$all = $PreSystem.split("`n")
# Loop over lines
for($i = 0; $i -lt $all.length-1; $i += 1) {
# Access distinct lines
$entry = "$($all[$i])"
# Get the different parameters
$market_code = $entry.substring(1,3)
$soc = $entry.substring(4,9)
# Hash the SOC element
$hash = [System.BitConverter]::ToString($md5.ComputeHash($utf8.GetBytes($soc)))
# Create desired format for each entry
$output = $hash.Replace("-","")+$soc.Replace(" ","") + "_" + $market_code + $all[$i]
# Write to file
"$output" | Out-File -Filepath C:\"C:\FILE.txt" -Append -encoding ASCII
}
Thanks in advance
You can create a custom table format using the tip explained here. Here is an example for Get-Process:
$a = #{Expression={$_.Name};Label="Process Name";width=25}, `
#{Expression={$_.ID};Label="Process ID";width=15}, `
#{Expression={$_.MainWindowTitle};Label="Window Title";width=40}
Get-Process | Format-Table $a
Basically, you build an expression through wich Format-Table will pipe
each row. Instead of taking care of the formating yourself for each row, you build a hash and pipe it through Format-Table.
It's still not quite clear to me what output you actually want to achieve, but maybe this will give you some idea.
One of the most convenient ways to get formatted string output is using the format operator (-f). You specify a format string with placeholders in curly brackets, and fill it with the values of an array:
PS C:\> '_{0}:{1}:{2}_' -f 'foo', 'bar', 'baz'
_foo:bar:baz_
Column widths can be specified in the format string like this:
PS C:\> '_{0,-5}:{1,7}:{2,-9}_' -f 'foo', 'bar', 'baz'
_foo : bar:baz _
As you can see, negative values align the column to the left, positive values align it to the right.
If there's a chance that a value is too long for the give column width you need to truncate it, e.g. with the Substring() method:
PS C:\> $s = 'barbarbar'
PS C:\> $len = [math]::Min(7, $s.Length)
PS C:\> '_{0,-5}:{1,7}:{2,-9}_' -f 'foo', $s.Substring(0, $len), 'baz'
_foo :barbarb:baz _
You can quickly have a fixed size left-aligned content string using the following code:
Write-Host "$myvariable $(" " * 60)".Substring(0,60)
this will give you a fixed width of 60 characters with the contents aligned to the left
One of the solutions is for each of the rows use this mechanism when concatenating:
$a = "MMA"
$b = "12345"
$str = "$($b)_$($a)"
if (($str.Length) -ge 13 ) {
Write-Host "$($str)"
} else {
$padStr = " " * (13 - ($str.Length))
Write-Host "$($str)$($padStr)"
}
So instead of Write-Host CmdLet you can use the appropriate CmdLet for your purpose.
Edit, after adding actual code. So the above logic would translate into:
$market_code = $entry.subString(1,3)
$soc = $entry.subString(4,9)
$str = $soc.Replace(" ", "") + "_" + $market_code
if (($str.Length) -ge 13 ) {
$output = $hash.Replace("-","") + $str + $all[$i]
} else {
$padStr = " " * (13 - ($str.Length))
$output = $hash.Replace("-","") + $str + $padStr + $all[$i]
}
You can do fixed size using next code:
$data = "Some text"
$size = 20
$str = [string]::new(' ',$size).ToCharArray()
$data.CopyTo(0,$str,0,$data.Length)
$str = $str -join ''

PowerShell: remove or replace quote marks from variable

I'm using Get-EventLog to set a variable, and then setting another variable with the event ID description. I then use blat.exe to email this information to a group.
The description contains quotation marks. The quotation marks are causing blat to exit with error.
Is there a way to remove the quotes from the event.Message and replace them with a space or something?
If the variable is a String object then you can do the following:
$Variable.Replace("`"","")
I actually just got it. The number of quotes and double quotes was confusing me, but this has worked and blat did not error.
$var -replace '"', ""
Those quotes are: single, double, single, comma, double, double.
Depending on a case, it might be simpler to use Trim(Char[]) method:
...Removes all leading and trailing occurrences...
e.g. $your_variable.Trim('"')
It will remove quotes only from start and end of $your_variable. It will keep any quotes, escaped or not, which are inside the text of $your_variable as they were:
PS C:\> $v.Trim('"') # where $v is: "hu""hu"hu'hu"
hu""hu"hu'hu
You can use Trim('"'), Trim("'"), but also both: Trim("`"'")
Note that Trim() does not care if a quote is orphaned, meaning that it will remove ending or starting quote regardless of it having or not a paired quote on the other side of the string.
PS C:\Users\Papo> $hu = "A: He asked `"whos this sofa?`" B: She replied: `"Chris'`""
PS C:\Users\Papo> $hu
A: He asked "whos this sofa?" B: She replied: "Chris'"
PS C:\Users\Papo> $hu.trim('"')
A: He asked "whos this sofa?" B: She replied: "Chris'
PS C:\Users\Papo> # and even worse:
PS C:\Users\Papo> $hu.trim("'`"")
A: He asked "whos this sofa?" B: She replied: "Chris
If you use Powershell's built-in send-mailmessage (2.0 required), you can eliminate your dependency on blat.exe and properly handle this issue without editing the description from the event log.
The problem is that a simple replace cleans out every quote character, even if escaped (doubled).
Here are the functions I created for my use :
one which removes only orphan quotes.
one which escapes them
I also made them generic to manage other characters, with the optional $charToReplace parameter
#Replaces single occurrences of characters in a string.
#Default is to replace single quotes
Function RemoveNonEscapedChar {
param(
[Parameter(Mandatory = $true)][String] $param,
[Parameter(Mandatory = $false)][String] $charToReplace
)
if ($charToReplace -eq '') {
$charToReplace = "'"
}
$cleanedString = ""
$index = 0
$length = $param.length
for ($index = 0; $index -lt $length; $index++) {
$char = $param[$index]
if ($char -eq $charToReplace) {
if ($index +1 -lt $length -and $param[$index + 1] -eq $charToReplace) {
$cleanedString += "$charToReplace$charToReplace"
++$index ## /!\ Manual increment of our loop counter to skip next char /!\
}
continue
}
$cleanedString += $char
}
return $cleanedString
}
#A few test cases :
RemoveNonEscapedChar("'st''r'''i''ng'") #Echoes st''r''i''ng
RemoveNonEscapedChar("""st""""r""""""i""""ng""") -charToReplace '"' #Echoes st""r""i""ng
RemoveNonEscapedChar("'st''r'''i''ng'") -charToReplace 'r' #Echoes 'st'''''i''ng'
#Escapes single occurences of characters in a string. Double occurences are not escaped. e.g. ''' will become '''', NOT ''''''.
#Default is to replace single quotes
Function EscapeChar {
param(
[Parameter(Mandatory = $true)][String] $param,
[Parameter(Mandatory = $false)][String] $charToEscape
)
if ($charToEscape -eq '') {
$charToEscape = "'"
}
$cleanedString = ""
$index = 0
$length = $param.length
for ($index = 0; $index -lt $length; $index++) {
$char = $param[$index]
if ($char -eq $charToEscape) {
if ($index +1 -lt $length -and $param[$index + 1] -eq $charToEscape) {
++$index ## /!\ Manual increment of our loop counter to skip next char /!\
}
$cleanedString += "$charToEscape$charToEscape"
continue
}
$cleanedString += $char
}
return $cleanedString
}
#A few test cases :
EscapeChar("'st''r'''i''ng'") #Echoes ''st''r''''i''ng''
EscapeChar("""st""""r""""""i""""ng""") -charToEscape '"' #Echoes ""st""r""""i""ng""
EscapeChar("'st''r'''i''ng'") -charToEscape 'r' #Echoes 'st''rr'''i''ng'
None of the above answers worked for me. So I created the following solution...
Search and Replace the character single Quote "'" ascii Character (39) with a space " " ascii Character (32)
$strOldText = [char] 39
$strNewText = [char] 32
$Variable. = $Variable..Replace($strOldText, $strNewText).Trim()