In this article I talk about how command history can fool us. So you think you're repeating that command with Up Arrow? Module precedence and Command precedence can cross our plans.
In the console we can avoid retyping a command we issued before thanks to command history. This means we can execute the same command over and over again by, either using the \<Up Arrow> or typing the first characters and hit \<F8> to cycle through the statements we typed or pasted recently.
When I looked at scheduled tasks at a computer, I got surprised the output of a command was very different the second time I executed.
The computer involved ran Windows 10 OS, and had a lot of PowerShell modules available.
This computer was replaced, but still on the network just to make sure we could reach files on its local drives in case we missed something in the migration process. We usually keep it for a week or two so we can get necessary stuff on a new computer. Scheduled tasks were copied to the replacement too, and needed to be disabled on the old computer.
I started with
Get-ScheduledTask to see what was running or ready to run.
We can't disable running tasks, so we stop those first, like this:
Get-ScheduledTask | Stop-ScheduledTask
And to check again, I tapped \<Up Arrow> twice, so I saw
Get-ScheduledTask again, and hit \<Enter>.
You see the output is completely different than before?
The Mystery revealed
Well, there happen to be two modules containing the
The PowerShellPack was created by James Brundage, and I had it on some computers since 2009. Usually I copy complete Module directories from one computer to another, so this one travelled with me for 8 years.
The ScheduledTasks module was created by Microsoft, and is in a default PowerShell installation.
When starting a console, neither of those modules is loaded. Since I'm on PS V5, when I call a command, the module is autoloaded ( that's with PS V3 and higher, BTW ).
When autoloading, PowerShell loops over the directories in the Module Path variable, in the order they are listed, and as soon as the requested module or command name is encountered, that one is loaded.
As you can see, D:\Documents\WindowsPowerShell\Modules is the first directory in my Module Path, and, as shown above, the PowerShellPack module is in there.
Microsoft's SheduledTasks module is in C:\Windows\System32\WindowsPowerShell\v1.0\Modules, which is third in the list.
So, after the first
Get-ScheduledTask, we can see the PowerShellPack module is loaded with
And we see that in our session the
Get-ScheduledTask command is found in the PowerShellPack module with
But, in the next step, I piped to
Stop-ScheduledTask. That one is not in the PowerShellPack module, so PowerShell went through the Module Path again, and loaded the ScheduledTasks module. An other
Get-Module shows that we have both modules imported now:
That module contains the needed command, and also another
As a consequence, I now have two
Get-ScheduledTask commands imported in my session!
When two commands with the same name are imported into a session, there are some rules to decide which one will be executed:
About Command Precedence
First, the command type is decisive. The order is:
- Native Windows command
Both commands are of the type function, so that's a tie. The next criterium dictates that the most recently added command is executed. In this case, that's the one from the ScheduledTasks module.
Get-ScheduledTaskauto-imports the PowerShellPack module and runs the function from that module
Get-ScheduledTask | Stop-ScheduledTaskloads the ScheduledTasks module and runs both functions from there
Get-ScheduledTaskruns from the ScheduledTasks module because that's added last
It wasn't lost, it just got mislaid
Strangely, when looking for a command, we get different information depending on the time we issue a
Get-Command. Executing this is enough to import a module if it isn't yet loaded.
Again, we find the
Get-ScheduledTask function in the PowerShellPack module, that's no surprise.
And the ScheduledTasks module has it too, as we already know.
But, let's tap \<Up Arrow> twice again, and \<Enter>.
The PowerShellPack module doesn't have a
We don't see the function now because, starting with PS V3, only the commands that will be run are shown.
The documentation on MSDN says we can force
Get-Command to show all available versions anyway using the
-All switch. But on my system that didn't work:
That's a bit misleading, because when we remove the ScheduledTasks module from the session and then
Get-Command again, it has magically returned.
Simply put, we need to be aware that command history contains the previously executed statements and resubmits them. It does not hold the history of the actually executed commands.
If we want to have more control over which command has to be executed, we can move our modules to directories further or earlier in the module path. This has its effects, but is cumbersome to maintain and works in only one direction. If next time I prefer another order between commands with the same name, I have to reorder again.
We can type
PowerShellPack\Get-ScheduledTask to make sure the command of our choice is executed.
About Command Precedence
This article contains one line that says it all:
If you specify the path to a command, Windows PowerShell runs the command at the location specified by the path.
We can also explicitly import a module and assign a prefix.