{"id":4351,"date":"2009-03-13T14:39:00","date_gmt":"2009-03-13T14:39:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/powershell\/2009\/03\/13\/dir-ad\/"},"modified":"2019-02-18T13:12:46","modified_gmt":"2019-02-18T20:12:46","slug":"dir-ad","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/powershell\/dir-ad\/","title":{"rendered":"dir &#8211;a:d"},"content":{"rendered":"<p>In cmd, listing files based on attributes was simple:<\/p>\n<table class=\"\" cellSpacing=\"0\" cellPadding=\"2\" width=\"400\" border=\"0\">\n<tbody>\n<tr>\n<td class=\"\" vAlign=\"top\" width=\"200\">only directories<\/td>\n<td class=\"\" vAlign=\"top\" width=\"200\">dir \/a:d<\/td>\n<\/tr>\n<tr>\n<td class=\"\" vAlign=\"top\" width=\"200\">only files (no directories)<\/td>\n<td class=\"\" vAlign=\"top\" width=\"200\">dir \/a:-d<\/td>\n<\/tr>\n<tr>\n<td class=\"\" vAlign=\"top\" width=\"200\">only hidden files<\/td>\n<td class=\"\" vAlign=\"top\" width=\"200\">dir \/a:h<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In PowerShell, it\u2019s not so easy:<\/p>\n<table class=\"\" cellSpacing=\"0\" cellPadding=\"2\" width=\"571\" border=\"0\">\n<tbody>\n<tr>\n<td class=\"\" vAlign=\"top\" width=\"200\">only directories<\/td>\n<td class=\"\" vAlign=\"top\" width=\"369\">dir | ? { $_.PSIsContainer }<\/td>\n<\/tr>\n<tr>\n<td class=\"\" vAlign=\"top\" width=\"200\">only files (no directories)<\/td>\n<td class=\"\" vAlign=\"top\" width=\"369\">dir | ? { !$_.PSIsContainer }<\/td>\n<\/tr>\n<tr>\n<td class=\"\" vAlign=\"top\" width=\"200\">only hidden files<\/td>\n<td class=\"\" vAlign=\"top\" width=\"369\">dir -force | ? { $_.Attributes \u2013band [IO.FileAttributes]::Hidden } <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>We have had requests to cover the first case better, for example:<\/p>\n<p><a title=\"https:\/\/connect.microsoft.com\/feedback\/ViewFeedback.aspx?SiteID=99&amp;FeedbackID=252549&amp;wa=wsignin1.0\" href=\"https:\/\/connect.microsoft.com\/feedback\/ViewFeedback.aspx?SiteID=99&amp;FeedbackID=252549&amp;wa=wsignin1.0\">https:\/\/connect.microsoft.com\/feedback\/ViewFeedback.aspx?SiteID=99&amp;FeedbackID=252549&amp;wa=wsignin1.0<\/a><\/p>\n<p><a title=\"https:\/\/connect.microsoft.com\/feedback\/ViewFeedback.aspx?SiteID=99&amp;FeedbackID=308796&amp;wa=wsignin1.0\" href=\"https:\/\/connect.microsoft.com\/feedback\/ViewFeedback.aspx?SiteID=99&amp;FeedbackID=308796&amp;wa=wsignin1.0\">https:\/\/connect.microsoft.com\/feedback\/ViewFeedback.aspx?SiteID=99&amp;FeedbackID=308796&amp;wa=wsignin1.0<\/a><\/p>\n<p>We haven\u2019t added such a parameter to Get-ChildItem yet, but with some new features in V2, you can add them yourself.<\/p>\n<p>The techniques I describe below can be used to augment any cmdlet, or any script or function for that matter.<\/p>\n<p>If you don\u2019t care much about the actual implementation details, just skip to the bottom and download the module.&nbsp; To start using it, you can use:<\/p>\n<pre class=\"PowerShellColorizedScript\"><span>import-module<\/span> <span>path\\to\\Get-ChildItem.psm1<\/span><\/pre>\n<p>(Note that there isn\u2019t any particularly good reason I implemented this as a module, other than as a way of testing multiple features.&nbsp; It could work equally well as a script or function.)<\/p>\n<p>To get started, we need a proxy to Get-ChildItem.&nbsp; It should support all of the parameters that Get-ChildItem supports, plus it should support pipelining in the same way as the cmdlet.&nbsp; We can use the following code to generate a proxy:<\/p>\n<pre class=\"PowerShellColorizedScript\"><span>$md<\/span> <span>=<\/span> <span>new-object<\/span> <span>System.Management.Automation.CommandMetadata<\/span> <span>(<\/span><span>get-command<\/span> <span>get-childitem<\/span><span>)<\/span>\n<span>[System.Management.Automation.ProxyCommand]<\/span><span>::<\/span><span>Create<\/span><span>(<\/span><span>$md<\/span><span>)<\/span><\/pre>\n<p>This will return a rather long string, we can capture it in a file and make our changes.<\/p>\n<p>The first thing we need is an additional parameter.&nbsp; We\u2019ll call it Attribute.&nbsp; We just add:<\/p>\n<p><span>[System.String]<\/span> <br \/><span>$Attribute<\/span> <span>=<\/span> <span>&#8221;<\/span><span>,<\/span><\/p>\n<p><span><\/span>to the param block.<\/p>\n<p>Next, we need to add the logic for our new parameter.&nbsp; To do this, we will replace the Begin block that was generated with the following code:<\/p>\n<p>&nbsp;<\/p>\n<p><font face=\"Courier New\" size=\"2\"><\/font><\/p>\n<p><span>[void]<\/span><span>$PSBoundParameters<\/span><span>.<\/span><span>Remove<\/span><span>(<\/span><span>&#8216;Attribute&#8217;<\/span><span>)<\/span> <br \/><span>$wrappedCmd<\/span> <span>=<\/span> <span>$ExecutionContext<\/span><span>.<\/span><span>InvokeCommand<\/span><span>.<\/span><span>GetCommand<\/span><span>(<\/span><span>&#8216;Get-ChildItem&#8217;<\/span><span>,<\/span><span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [System.Management.Automation.CommandTypes]<\/span><span>::<\/span><span>Cmdlet<\/span><span>)<\/span> <br \/><span>if<\/span> <span>(<\/span><span>$Attribute<\/span> <span>-ne<\/span> <span>&#8221;<\/span><span>)<\/span> <br \/><span>{<\/span> <br \/>&nbsp;&nbsp;&nbsp; <span>$includeMask<\/span> <span>=<\/span> <span>0<\/span> <br \/>&nbsp;&nbsp;&nbsp; <span>$excludeMask<\/span> <span>=<\/span> <span>0<\/span> <br \/>&nbsp;&nbsp;&nbsp; <span>[bool]<\/span><span>$onOrOff<\/span> <span>=<\/span> <span>$true<\/span> <br \/>&nbsp;&nbsp;&nbsp; <span>foreach<\/span> <span>(<\/span><span>$char<\/span> <span>in<\/span> <span>$Attribute<\/span><span>.<\/span><span>GetEnumerator<\/span><span>(<\/span><span>)<\/span><span>)<\/span> <br \/>&nbsp;&nbsp;&nbsp; <span>{<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if<\/span> <span>(<\/span><span>$char<\/span> <span>-eq<\/span> <span>&#8216;-&#8216;<\/span><span>)<\/span> <span>{<\/span> <span>$onOrOff<\/span> <span>=<\/span> <span>$false<\/span> <span>}<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>else<\/span> <span>{<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if<\/span> <span>(<\/span><span>$flags<\/span><span>[<\/span><span>$char<\/span><span>]<\/span> <span>-eq<\/span> <span>$null<\/span><span>)<\/span> <span>{<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>throw<\/span> <span>&#8220;Attribute &#8216;$char&#8217; not supported&#8221;<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>}<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if<\/span> <span>(<\/span><span>$onOrOff<\/span><span>)<\/span> <span>{<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>$includeMask<\/span> <span>=<\/span> <span>$includeMask<\/span> <span>-bor<\/span> <span>$flags<\/span><span>[<\/span><span>$char<\/span><span>]<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>}<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>else<\/span> <span>{<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>$excludeMask<\/span> <span>=<\/span> <span>$excludeMask<\/span> <span>-bor<\/span> <span>$flags<\/span><span>[<\/span><span>$char<\/span><span>]<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>}<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>$onOrOff<\/span> <span>=<\/span> <span>$true<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>}<\/span> <br \/>&nbsp;&nbsp;&nbsp; <span>}<\/span> <\/p>\n<p>&nbsp;&nbsp;&nbsp; <span>if<\/span> <span>(<\/span><span>$includeMask<\/span> <span>-band<\/span> <span>[IO.FileAttributes]<\/span><span>::<\/span><span>Hidden<\/span><span>)<\/span> <span>{<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>$PSBoundParameters<\/span><span>.<\/span><span>Force<\/span> <span>=<\/span> <span>$true<\/span> <br \/>&nbsp;&nbsp;&nbsp; <span>}<\/span> <\/p>\n<p>&nbsp;&nbsp;&nbsp; <span>$scriptCmd<\/span> <span>=<\/span> <span>{<\/span><span>&amp;<\/span> <span>$wrappedCmd<\/span> <span>@PSBoundParameters<\/span> <span>|<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>?<\/span> <span>{<\/span> <span>$_<\/span><span>.<\/span><span>PSProvider<\/span><span>.<\/span><span>Name<\/span> <span>-ne<\/span> <span>&#8216;FileSystem&#8217;<\/span> <span>-or<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>(<\/span><span>(<\/span><span>(<\/span><span>$_<\/span><span>.<\/span><span>Attributes<\/span> <span>-band<\/span> <span>$includeMask<\/span><span>)<\/span> <span>-eq<\/span> <span>$includeMask<\/span><span>)<\/span> <span>-and<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>(<\/span><span>(<\/span><span>$_<\/span><span>.<\/span><span>Attributes<\/span> <span>-band<\/span> <span>$excludeMask<\/span><span>)<\/span> <span>-eq<\/span> <span>0<\/span><span>)<\/span><span>)<\/span> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>}<\/span> <br \/>&nbsp;&nbsp;&nbsp; <span>}<\/span> <br \/><span>}<\/span> <br \/><span>else<\/span> <span>{<\/span> <br \/>&nbsp;&nbsp;&nbsp; <span>$scriptCmd<\/span> <span>=<\/span> <span>{<\/span><span>&amp;<\/span> <span>$wrappedCmd<\/span> <span>@PSBoundParameters<\/span> <span>}<\/span> <br \/><span>} <br \/><\/span><br \/><span>$steppablePipeline<\/span> <span>=<\/span> <span>$scriptCmd<\/span><span>.<\/span><span>GetSteppablePipeline<\/span><span>(<\/span><span>)<\/span> <br \/><span>$steppablePipeline<\/span><span>.<\/span><span>Begin<\/span><span>(<\/span><span>$PSCmdlet<\/span><span>)<\/span> <\/p>\n<p>&nbsp;<\/p>\n<p>Note the first thing we do is remove our parameter from PSBoundParameters.&nbsp; If we don\u2019t do that, the real cmdlet Get-ChildItem will complain about the unknown parameter when it is specified.<\/p>\n<p>I wanted functionality and syntax as close to cmd.exe as possible.&nbsp; So we should support things like \u2018dir \u2013a:h-sr\u2019, which would include hidden read-only files that are not system files.&nbsp; To do this, I generate bitmasks representing the attributes requested, then filter the output of the actual Get-ChildItem command based on these bitmasks.<\/p>\n<p>Note the script block assigned to $scriptCmd is similar to the one generated by ProxyCommand.Create, but it includes some filtering.&nbsp; Steppable pipelines can\u2019t be created for arbitrary script blocks, only when there is a single pipeline in the script block.&nbsp; Fortunately we can do all our filtering in one pipeline.<\/p>\n<p>If you wanted to add sorting to the output, you could extend this pipeline to do the appropriate sorting.&nbsp; I leave it as an exercise for the reader to add the cmd.exe sorting flags like \/OD, \/O-D, \/OS, \/O-S, etc.<\/p>\n<p>ProxyCommand.Create works pretty good, but it doesn\u2019t generate code to support dynamic parameters.&nbsp; Most cmdlets don\u2019t have dynamic parameters, but Get-ChildItem definitely does, so we want our proxy to support them as well.&nbsp; Different providers can add dynamic parameters to Get-ChildItem, so the optimal solution is to, at runtime, ask the Get-ChildItem cmdlet what dynamic parameters it has.&nbsp; We can do that this way:<\/p>\n<pre class=\"PowerShellColorizedScript\"><span>[void]<\/span><span>$PSBoundParameters<\/span><span>.<\/span><span>Remove<\/span><span>(<\/span><span>'Attribute'<\/span><span>)<\/span>\n<span>$argList<\/span> <span>=<\/span> <span>@(<\/span><span>$psboundparameters<\/span><span>.<\/span><span>getenumerator<\/span><span>(<\/span><span>)<\/span> <span>|<\/span> <span>%<\/span> <span>{<\/span> <span>\"-$($_.Key)\"<\/span><span>;<\/span> <span>$_<\/span><span>.<\/span><span>Value<\/span> <span>}<\/span><span>)<\/span>\n<span>$wrappedCmd<\/span> <span>=<\/span> <span>Get-Command<\/span> <span>Get-ChildItem<\/span> <span>-Type<\/span> <span>Cmdlet<\/span> <span>-ArgumentList<\/span> <span>$argList<\/span>\n<span>$providerParams<\/span> <span>=<\/span> <span>@(<\/span><span>$wrappedCmd<\/span><span>.<\/span><span>Parameters<\/span><span>.<\/span><span>GetEnumerator<\/span><span>(<\/span><span>)<\/span> <span>|<\/span>\n                                                  <span>Where-Object<\/span> <span>{<\/span> <span>$_<\/span><span>.<\/span><span>Value<\/span><span>.<\/span><span>IsDynamic<\/span> <span>}<\/span><span>)<\/span>\n<span>if<\/span> <span>(<\/span><span>$providerParams<\/span><span>.<\/span><span>Length<\/span> <span>-gt<\/span> <span>0<\/span><span>)<\/span>\n<span>{<\/span>\n    <span>$paramDictionary<\/span> <span>=<\/span> <span>new-object<\/span> <span>Management.Automation.RuntimeDefinedParameterDictionary<\/span>\n    <span>foreach<\/span> <span>(<\/span><span>$param<\/span> <span>in<\/span> <span>$providerParams<\/span><span>)<\/span>\n    <span>{<\/span>\n        <span>$param<\/span> <span>=<\/span> <span>$param<\/span><span>.<\/span><span>Value<\/span>\n        <span>$dynParam1<\/span> <span>=<\/span> <span>new-object<\/span> <span>Management.Automation.RuntimeDefinedParameter<\/span> <span>`<\/span>\n                                  <span>$param<\/span><span>.<\/span><span>Name<\/span><span>,<\/span> <span>$param<\/span><span>.<\/span><span>ParameterType<\/span><span>,<\/span> <span>$param<\/span><span>.<\/span><span>Attributes<\/span>\n        <span>$paramDictionary<\/span><span>.<\/span><span>Add<\/span><span>(<\/span><span>$param<\/span><span>.<\/span><span>Name<\/span><span>,<\/span> <span>$dynParam1<\/span><span>)<\/span>\n    <span>}<\/span>\n    <span>return<\/span> <span>$paramDictionary<\/span>\n<span>}<\/span><\/pre>\n<p>This code generates an argument list from the automatic variable $PSBoundParameters.&nbsp; $argList won\u2019t necessarily look exactly like the arguments passed in as it will add the parameter names to positional arguments.<\/p>\n<p>Jason Shirk [MSFT]<\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"https:\/\/msdnshared.blob.core.windows.net\/media\/MSDNBlogsFS\/prod.evol.blogs.msdn.com\/CommunityServer.Components.PostAttachments\/00\/09\/47\/35\/06\/Get-ChildItem.psm1\">Get-ChildItem.psm1<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In cmd, listing files based on attributes was simple: only directories dir \/a:d only files (no directories) dir \/a:-d only hidden files dir \/a:h In PowerShell, it\u2019s not so easy: only directories dir | ? { $_.PSIsContainer } only files (no directories) dir | ? { !$_.PSIsContainer } only hidden files dir -force | ? [&hellip;]<\/p>\n","protected":false},"author":600,"featured_media":13641,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4351","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell"],"acf":[],"blog_post_summary":"<p>In cmd, listing files based on attributes was simple: only directories dir \/a:d only files (no directories) dir \/a:-d only hidden files dir \/a:h In PowerShell, it\u2019s not so easy: only directories dir | ? { $_.PSIsContainer } only files (no directories) dir | ? { !$_.PSIsContainer } only hidden files dir -force | ? [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/4351","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/users\/600"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/comments?post=4351"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/4351\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media\/13641"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media?parent=4351"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/categories?post=4351"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/tags?post=4351"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}