Get string length that is created dynamically - powershell

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)))"

Related

powershell (Get-WmiObject win32_physicalmedia).serialnumber output hex

When I used (Get-WmiObject win32_physicalmedia).serialnumber the output was in hex. Example: 31323334353637383930. Then then I used the code below
$pass=""
$t=(Get-WmiObject win32_physicalmedia).serialnumber
$t -split '(.{2})' |%{ if ($_ -ne "") { $pass+=[CHAR]([CONVERT]::toint16("$_",16)) }}
write host $pass
The output was: 1234567890. The problem is that 1234567890 is not the serial number -- the real serial number is 2143658709. I need a script to swap the number $input "1234567890" to $output "214365768709".
this presumes your SN string is an even number of characters, and that the real number simply reverses the character pairs.
$InString = '1234567890'
$OutString = ''
foreach ($Index in 0..($InString.Length / 2))
{
$CurPos = $Index * 2
$OutString += $InString[$CurPos + 1] + $InString[$CurPos]
}
$OutString
output = 2143658709
I think this is called "middle endian" format, where every two bytes are reversed: middle-endian
Coming from a post here: WMI Win32_PhysicalMedia SMART ID in Vista and 7 Permissions

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.

How to concatenate array of integers into comma separated string

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(",")

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 Using Backtick for new lines - weirdness

What exaxctly is happening in example 1? How is this parsed?
# doesnt split on ,
[String]::Join(",",("aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa," + `
"aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa".Split(',') `
| foreach { ('"' + $_ + '"') }))
# adding ( ) does work
[String]::Join(",",(("aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa," + `
"aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aaaaa").Split(',') `
| foreach { ('"' + $_ + '"') }))
In you first example you may remove the backtick, because Powershell knows that the string will continue (there is a + sign).
What posh does
takes string "aaaa,aaaa..." (1) from first
evaluates the expression with split - it returns array of strings (from "aaaa,...aaaa".Split(','))
converts the array of strings to string, which returns again string "aaaa,...aaaa"
adds results from 1. and 3.
Note: when posh converts array to string, it uses $ofs variable. You will see it better in action when you will try this code:
$ofs = "|"
[String]::Join(",", ("aaaaa,aaaaa" + "bbbb,bbbb,bbbb".Split(',') | foreach { ('"' + $_ + '"') }))
Your first example only has the Split method applied to the second string of a's. The parentheses are necessary for order of operations. The Split method is performed before the concatenation in your first example.