If you have worked with PowerShell for a while, chances are you are familiar with Enums. If you have also worked with more traditional programming languages you definitely are familiar with Enums!
Enums can have custom attributes, and one such attribute is the topic this quick tip; the FlagsAttribute!
This custom attribute is used to indicate that the enumeration can be treated as a bit field, and are often used for lists of elements that might occur in combination, as opposed to normal enumerations that are constants, which are mutually exclusive.
An example of an enumeration that use the FlagsAttribute are System.IO.FileAttributes, while System.DayOfWeek are an example of a enumeration that don’t have this custom attribute. It’s quite obvious that when using the day of the week enumeration, you would only ever need one at the time, while a file can have multiple attributes.
To check if an enumeration have the FlagsAttribute, you can use the following code:
[bool]($enum.CustomAttributes | Where-Object {$_.AttributeType.Name -eq 'FlagsAttribute'})
To check the two enumerations I have used as examples, use the following two lines:
[bool]([System.DayOfWeek].CustomAttributes | Where-Object {$_.AttributeType.Name -eq 'FlagsAttribute'}) [bool]([System.IO.FileAttributes].CustomAttributes | Where-Object {$_.AttributeType.Name -eq 'FlagsAttribute'})
The first one should return False, while the last one should return True.
To check an enumeration with FlagsAttributes for the presence of a particular value (or flag), consider the following example:
$fileAttributes = [System.IO.FileAttributes]::Archive $fileAttributes -band [System.IO.FileAttributes]::Archive $fileAttributes -band [System.IO.FileAttributes]::Directory
First we define our own object of file attributes with just one attribute set. We then check for the presence of the Archive attribute first, then the Directory attribute. The result of the first test should be Archive, while the last line should return 0.
So how do we check for multiple attributes then? Let’s assume we have a file at ‘c:\temp’ called ‘testfile.txt’. This file have the Archive, Hidden and ReadOnly attributes set.
$file = Get-ChildItem -Path C:\temp\testfile.txt -Hidden $attributesToTestFor = [System.IO.FileAttributes]::Hidden $attributesToTestFor = $attributesToTestFor -bor [System.IO.FileAttributes]::Archive $file.Attributes -band $attributesToTestFor ($file.Attributes -band $attributesToTestFor) -eq $attributesToTestFor
So, the first thing we need to do is to get the FileInfo object for the file in question (line 1). Note that we need to use the Hidden parameter since the file is hidden. Then we start to build our collection of attributes that we want to check for (lines 2-3). Next we see that the code on line 4 just returns the attributes that we are checking for, so for it to be a proper test, we need to modify it a little (line 5). This should return True. Note that we didn’t test for the ReadOnly attribute, just the presence of the Hidden and Archive attributes.
A point of interest in this example is that the variable that we created, attributesToTestFor, is itself an object of type FileAttributes (System.IO.FileAttributes). So we are dynamically creating our own limited version of the FileAttributes object that we use to test the file attributes of the file.
To add more than one attribute we need to use the -bor (bitwise or) operator. Likewise, to check for the presence of the attributes we are using the -band (bitwise and) operator.
Lastly, I’d like to show you another way of doing the same test. This time using a built-in method called HasFlag. This method is new in .NET 4.5 I believe, and can be used to make the code a little more readable:
$file.Attributes.HasFlag($attributesToTestFor)
This line of code, will also return True. Note though, that the HasFlag method is somewhat slower than performing the bitwise check “manually”, so if you are concerned with optimizing your code this is something to keep in mind.
I hope this post have been informational. Please let me know if you have spotted any obvious (or not so obvious) errors, or if you just found it helpful!