When should 'Unknown' be used as -Encoding parameter? - powershell

I was thinking Unknown option is used for binary files concatenation.
http://technet.microsoft.com/en-us/library/dd315299.aspx
Unknown The encoding type is unknown
or invalid. The data can be treated as
binary.
But {Get-Content binary.dat -Encoding Unknown} doesn't return byte array but string array.
PS > $a = Get-Content $PSHOME\WTRInstaller.ico -Encoding Unknown
PS > $b = Get-Content $PSHOME\WTRInstaller.ico -Encoding Byte
PS > $a[0].GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
PS > $b[0].GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Byte System.ValueType
Even if I convert $a to byte array, it doesn't coincide with $b.
PS > [Text.Encoding]::Unicode.GetBytes($a)
PS > compare $c[0..10] $b[0..10]
InputObject SideIndicator
----------- -------------
10 =>
32 <=
When should 'Unknown' be used?

Uknown is not something that is supplied, but rather something that is returned. System.Text.Encoding is not just for use by powershell. There are many areas in the BCL that accept or return the same enums. Some values are for return, others for supplying.
-Oisin

Related

powershell hashtable using key named length

I'm got a problem parsing a file with key value pairs when one of the keys is named "length". Piping the content to ConvertFrom-StringData creates a hashtable with the key called "length" but, when I try to access it, I get the length of the table instead. Turns out this is because ConvertFrom-StringData is returning an array of hashtables and Length is the length of the array (6 in this case).
Any idea how to get around this? For a regular hashtable you can create a key called length and access it just fine ($tmp[1].length gives 1000um as it should). I won't usually know the index of the "length" field in the file, however.
> $tmp = Get-Content "Sample Settings.txt"
> $tmp
device=Hall bar
length=1000um
width=500um
thickness=8nm
system=PPMS
field=Perpendicular
> $tmp = $tmp | ConvertFrom-StringData
> $tmp
Name Value
---- -----
device Hall bar
length 1000um
width 500um
thickness 8nm
system PPMS
field Perpendicular
> $tmp.length
6
> $tmp[1].length
1000um
> $tmp.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
> $tmp[0].GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Hashtable System.Object
To get regular hashtable, use -Raw switch as follows:
$tmp = Get-Content "Sample Settings.txt" -Raw | ConvertFrom-StringData
Then:
$tmp
<#
Name Value
---- -----
device Hall bar
thickness 8nm
field Perpendicular
width 500um
system PPMS
length 1000um
<##>
$tmp.gettype().Name
# Hashtable
$tmp.Count
# 6
$tmp.length
# 1000um
Use IndexOf() to get the index and then use it to get the value:
$li = $tmp.keys.IndexOf('length')
$tmp[$li].length
Use -replace with a RegEx to prepend length with a string (here my):
> $tmp = $tmp -replace '^(?=length)','my' | convertfrom-stringdata
> $tmp
Name Value
---- -----
device Hall bar
mylength 1000um
width 500um
thickness 8nm
system PPMS
field Perpendicular
> $tmp.mylength
1000um
(?=..) is a zero length look ahead assertion

What are the rules for automatic arrays wrapping/unwrapping?

Consider this code:
$a = '[{"a":"b"},{"c":"d"}]'
"Test1"
$a | ConvertFrom-Json | ForEach-Object { $_.GetType() }
"Test2"
$b = $a | ConvertFrom-Json
$b | ForEach-Object { $_.GetType() }
This produces the following output:
Test1
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Test2
True False PSCustomObject System.Object
True False PSCustomObject System.Object
Evidently, if we use a temporary variable, whatever is passed down the pipeline is not the same thing that is passed if we do not use one.
I would like to know what the rules that powershell uses for automatic array wrapping / unwrapping are, and if the using a temp var the best course of action if we need to iterate through a json array.
Update 1
Logically ConvertFrom-Json should return an array with the input given and ForEach-Object should iterated on the said array. However in the first test this does not happen. Why?
Update 2
Is it possible that it's ConvertFrom-Json specific? Like bug/issue?
There is only one rule with regard to pipeline items' unwrapping: all arrays and collections written to the pipeline are always getting unwrapped to the items ("unwrapped one level down" or "unwrapped in a non-recursive fashion" would be more correct statement but for the sake of simplicity we are not going to consider nested arrays so far).
There is still a possibility to override this behavior by using unary comma operator:
$a = 1,2
$a | ForEach-Object{ $_.GetType() }
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
True True Int32 System.ValueType
,$a | ForEach-Object{ $_.GetType() }
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
In the second case Powershell pipeline engine unwrapped $a but afterwards the result got wrapped back to array by , operator.
As to ConvertFrom-Json case I personally believe that its observed behavior is more predictable as it allows you to capture JSON arrays as a whole by default.
If you are interested in the details, the function Get-WrappedArray in the code below imitates ConvertFrom-Json's behavior:
function Get-WrappedArray {
Begin { $result = #() }
Process { $result += $_ }
End { ,$result }
}
$a | Get-WrappedArray | ForEach-Object{ $_.GetType() }
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
$b = $a | Get-WrappedArray
$b | ForEach-Object{ $_.GetType() }
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
True True Int32 System.ValueType

Powershell split by '_' return empty

So i have folder with several files:
$files = #(Get-ChildItem "myPath")
I can see via the debugger that $files contains several items and i want to take the first:
$files[0] = "123_this.is.string"
And i want to split in by '_' and take 123
$splitted = $files[0] -split "_"
So here i can see that $splitted is empty.
Any suggestions why this strange behavior ?
$files[0] isn't a string but a FileSystemInfo Object.
$files[0].getType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True FileInfo System.IO.FileSystemInfo
So to get it work you have to use the split function to the filename of the file which is a string.
$files[0].name.getType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
With this it should work:
$files[0].name.split("_")
Try:
$files[0].ToString().split("_")

powershell first argument is of type object[]?

If i run the following code in PowerShell ISE
cls
Function XmlTransformaton ($sourceFile, $targetFile, $xsltFile)
{
echo "sourceFile: " + $sourceFile.GetType();
echo "targetFile: $targetFile";
echo "xsltFile: $xsltFile";
}
XmlTransformaton("C:\temp\TransfromTest\Test.rdl", "C:\temp\TransfromTest\Test.rdl", "C:\temp\TransfromTest\Test.xslt");
i get the following output
sourceFile:
+
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
targetFile:
xsltFile:
Why on earth is the first argument of type array? The result of this is that all other parameters are empty!
Because you're passing an array as your first (and only) argument. It looks like you want to be doing this instead:
XmlTransformaton "C:\temp\TransfromTest\Test.rdl" "C:\temp\TransfromTest\Test.rdl" "C:\temp\TransfromTest\Test.xslt"
Which gives:
sourceFile:
+
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
targetFile: C:\temp\TransfromTest\Test.rdl
xsltFile: C:\temp\TransfromTest\Test.xslt
Multiple arguments are passed to functions as Some-Function $param1 $param2 $paramN, no need to use parenthesis and comma separated args - that's how you construct an array

Create an array, hashtable and dictionary?

What is the proper way to create an array, hashtable and dictionary?
$array = [System.Collections.ArrayList]#()
$array.GetType() returns ArrayList, OK.
$hashtable = [System.Collections.Hashtable]
$hashtable.GetType() returns RuntimeType, Not OK.
$dictionary = ?
How to create a dictionary using this .NET way?
What is the difference between dictionary and hashtable? I am not sure when I should use one of them.
The proper way (i.e. the PowerShell way) is:
Array:
> $a = #()
> $a.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Hashtable / Dictionary:
> $h = #{}
> $h.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Hashtable System.Object
The above should suffice for most dictionary-like scenarios, but if you did explicitly want the type from Systems.Collections.Generic, you could initialise like:
> $d = New-Object 'system.collections.generic.dictionary[string,string]'
> $d.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Dictionary`2 System.Object
> $d["foo"] = "bar"
> $d | Format-Table -auto
Key Value
--- -----
foo bar
If you want to initialize an array you can use the following code:
$array = #() # empty array
$array2 = #('one', 'two', 'three') # array with 3 values
If you want to initialize hashtable use the following code:
$hashtable = #{} # empty hashtable
$hashtable2 = #{One='one'; Two='two';Three='three'} # hashtable with 3 values
Hashtable and dictionary in Powershell is pretty much the same, so I suggest using hashtable in almost all cases (unless you need to do something in .NET where Dictionary is required)