While trying to come up with a solution to the new brainteaser on the Powershellmagazine.com I’ve found out you can quickly get the value of ‘LastWriteTime’ property of file or directory just by piping a ‘System.IO.FileSystemInfo’ object to ‘date’, as such:

gi .|date
Tuesday, November 20, 2012 9:58:12 AM

Translating to:

Get-Item . | Get-Date
Tuesday, November 20, 2012 9:58:12 AM

The ‘.’ represents the current folder as usual and can be of course replaced with path to any folder or file. If you wonder the trick also works for the ‘Get-ChildItem’ cmdlet because it also returns objects of the mentioned type, but you won’t be getting any file names making the output hard to follow.

Command discovery

I hit the trick while trying if the ‘Get-Date’ can be shortened, without checking if there is such alias first. I was amazed it works and returns the same output. Logically I assumed ‘date’ has to be an alias for the command, but searching the list of aliases returned no result:

Get-Alias '*date*'

Strange. Confirmed my syntax running the same command looking for the ‘gci’ alias that I know exists:

Get-Alias '*gci*'
CommandType Name                 ModuleName
----------- ----                 ----------
Alias       gci -> Get-ChildItem

Ok, it has to be something else. I took another try with the ‘Get-Command’ cmdlet, only to recieve an error:

Get-Command date
Get-Command : The term 'date' is not recognized as the name of a cmdlet,
function, script file, or operable program. ...

Again, confirmed the syntax with another command:

Get-Command ping
CommandType Name     ModuleName
----------- ----     ----------
Application PING.EXE

Weird. The trick is working so there must be other way to alias a command or a native command called ‘date’, both not showing in appropriate lists. The second case is easy to eliminate, native commands return string on their output and checking for the type is as easy as calling the ‘GetType’ method on the object:

(gi .|date).GetType()
IsPublic IsSerial Name     BaseType
-------- -------- ----     --------
True     True     DateTime System.ValueType

The output type name is ‘DateTime’ not ‘String’ so the output must be made by some PowerShell cmdlet or call to .NET; there must be something somehow bond to the ‘date’ without alias. Taking the last resort I decided to trace the command. I could have done it in the first place but reading through piles of output or learning the solution right away (if you know what to search for) is certainly less fun than figuring it out by eliminating other choices. And then confirming it by trace.
If you run the following command be prepared for extensive amount of data:

Trace-Command -PSHost -Option all -Command date -Name *

Take the output and search it for ‘Get-Date’, which was the main suspect from the beginning, and luckily find this few lines ahead of the first match:

DEBUG: CommandDiscovery Information: 0 : The command [date] was not found,
trying again with get- prependedConsoleHost
Information: 0 :  WriteLine   FalseConsoleControl Information: 0 :  WriteLine   3

Turns out PowerShell is trying to find the ‘date’ command and because there is none it concatenates it with ‘Get-’, tries again and finds the ‘Get-Date’ cmdlet. That explains it.

Now that you know the source of the message you can inspect how PowerShell does it, tracing only the ‘CommandDiscovery’ source.

Trace-Command -PSHost -Option all -Command date -Name CommandDiscovery

I won’t post output of this command either because it is still really long, but if you go through it you’ll see that PowerShell tries to find the command by name first. Then it goes through all folders in the ‘$env:Path’ variable trying to find the correct file adding ‘.ps1′,’.psm1′,’.psd1′ and the extensions in the ‘$env:PathExt’ to the ‘date’ hoping to create a correct file path. If nothing is found it adds the ‘Get-’ prefix to the command name and repeats the whole process. If no command is found an exception is thrown. In our case (assuming there is no earlier match) the ‘Get-Date’ cmdlet is found:

DEBUG: CommandDiscovery Information: 0 : Cmdlet found: Get-Date
Microsoft.PowerShell.Commands.GetDateCommand

I haven’t found why the process stops at ‘Get-’ and doesn’t progress to ‘Set-’, ‘Remove-’ etc., but I assume it is to prevent changing anything unexpectedly. If the ‘Get-’ prefix is used properly the worst thing that should happen when executing any ‘Get-Anything’ command is retrieving some unnecessary data. If you’d progress to ‘Set-’, ‘Remove-’ or maybe ‘Restart-’ you could break something very easily.

Conclusion

I have to admit my assumptions for my final solution proposal were incorrect.

ps -id $pid|gi|date

I assumed the only restriction to make this work was running the commands in PowerShell Console host but turns out you also mustn’t have any file named ‘date’ with any of the mentioned extensions placed in any of the paths listed in the Path environment variable. Otherwise you could finish with this:

ps -id $pid|gi|date
I am tired of looking up the commands for you, stop being lazy!

I have to say I am really enjoying the Brainteasers series.