I am struggling with Powershell to create a custom type that has a bit-combination like file-attributes. The usage of the type should work later like this:
$attr = [myType]::new()
$attr.read = $true
$attr.write = $true
$attr.execute = $true
write-host $attr # or $attr.toString()? -> read,write,execute
write-host $attr.value__ # -> 7 (3 bits set to 1)
My final type will need 6 bits. I want to skip the part with manual bit-operations in my later code to get each bit back. So, I want to have it in the type, if possible. Any ideas how to get this done?
For this you might use the PowerShell Enumerations as flags feature:
Enumerations as flags
Enumerations can be defined as a collection of bit flags. Where, at any given point the enumeration represents one or more of those flags turned on.
For enumerations as flags to work properly, each label should have a power of two value.
In the following example the Access enumeration is created
[Flags()] enum Access {
Execute = 1
Write = 2
Read = 4
}
Taking advantage of the PowerShell implicit type casting where generally the left-hand-side of the operator dictates the type casting of the right-hand-side (respecting the operator precedence) you might use the flag enum
as follows:
Assigning
To assign bits (flags) to the [Access]
type, you might use a simple string array:
$Access = [Access]'Read', 'Execute'
Or as commented by @mklement0, use a single string:
$Access = [Access]'Read, Execute'
Reading
To read (and/or display) the assigned access:
$Access
Execute, Read
[int]$Access
5
Setting
To set a bit (flag) of the [Access]
type, you might use the binary or (-bor
) operator:
$Access -bor 'Write'
Execute, Write, Read
Resetting
To reset a bit (flag) of the [Access]
type, you might use the binary and (-band
) and a binary invertor (-bnot
) operator:
$Access -band -bnot [Access]'Read'
Execute
(note that you will need to explicitly set the type for RHS of the -not
operator)
Testing
To test that a specific bit is set, you can use the binary comparison operator -band
. In this example, we test for the [Access]
attributes in the value of $Access
.
If ($Access -band 'Read') { 'You have read access' }
'You have read access'
As noted by @mclayton this will not work for multiple flags in once:
$Access -band ([Access]'read, write')
Read
Returns Read
which implicitly casts to $True
, see: Booleans\Converting from collection types.
In other words, if you want to make sure that all concerned bits (flags) are set in the $Access
Value, you probably want to use the HasFlag
method instead:
$Access.HasFlag([Access]'read, write')
False
Thank you @mklement0 for the list of notable enum-relevant GitHub issues (as of PowerShell 7.4.0-preview.3):