Powershell equivalent for common Linux/bash commands

The majority of my colleagues have more of a Linux background than Windows. So their “cat” and their “grep” are near and dear to their heart and their first reflex when they get into PowerShell is to replicate these commands.

First, this is not always a good approach because bash and Powershell are fundamentally different. When you run bash commands or external executables in bash, you get plain text. When you run PowerShell cmdlets you get objects. So quite often, translating the bash way of doing things to PowerShell is the bad way of doing things. Powershell gives you rich objects with properties and methods to easily extract the information you need and/or to manipulate them in all sorts of ways. Use this to your advantage !

Still, I’m going to do this translation exercise for a few basic commands because it can be an interesting learning exercise for bash users coming to PowerShell. Besides, this is an opportunity to illustrate fundamental differences between bash and PowerShell.

pwd :

Get-Location. By the way, PowerShell has been designed to be user-friendly, even “old-school-Unix-shell-user”-friendly, so there are built-in aliases for popular Linux/bash commands which are pointing to the actual cmdlet. For example, bash users can still let their muscle memory type “pwd”, because it is an alias to the cmdlet Get-Location. Other alias to this cmdlet : “gl”.

cd :

The cmdlet is Set-Location, but you can use the aliases “cd”, “sl” or “chdir” if you have old habits or to save typing when you are using PowerShell interactively at the console.

ls :

Get-ChildItem. Conveniently, there is the built-in “ls” alias for those who come from bash and “dir” for those who come from cmd.exe.
A parameter combination which is frequently used is ls -ltr, it sorts the items to get the most recently modified files at the end.
The PowerShell equivalent would be :

 Get-ChildItem | Sort-Object -Property LastWriteTime 

Get-ChildItem Sort-Object

Notice how close to plain English the syntax is. This helps you to “Think it, type it, get it” as Jeffrey Snover likes to say.
Sorting is based on a property, not a column, so you don’t need to know the column number, you just need the property name.

find :

Get-ChildItem, but this time with the -Include (or -Filter) and -Recurse parameters.
For example, a common use case of find is to look recursively for files which have a case-insensitive string in their name.

 find . -type f -iname "snapshot" 

PowerShell is case-insensitive in general, so we have nothing in particular to do in this regard :

 Get-ChildItem -Filter "*snapshot*" -Recurse -File 

cp :

Copy-Item. Let’s say you want to copy a folder called “HolidayPictures” including all its sub-directories to your home directory, in bash you run :

cp -R HolidayPictures ~/ 

In Powershell on a windows machine, you would run :

 Copy-Item -Path ".\HolidayPictures\" -Destination "$env:USERPROFILE\" -Recurse

“$env:” is a scope modifier, it tells PowerShell to look for the variable named USERPROFILE in a special scope : the environment. This is a convenient way to use environment variables.

In addition, the -Path and -Destination parameters are positional. -Path is at position 1 and -Destination is at position 2, so the following command will do the same as the previous one :

 Copy-Item ".\HolidayPictures\" "$env:USERPROFILE\" -Recurse

To save even more typing, you could use the aliases “cp”, “copy” or “cpi”.

rm :

Here is the equivalent of the (in)famous rm -rf :

 Remove-Item -Recurse -Force 

And if you tell me it’s too much typing, I’m going to throw aliases at you : “rm”, “ri”, “rmdir”, “rd” and “del”.

mkdir :

 New-Item -ItemType directory -Name "MyNewFolder"

It does create the parent if needed without any special parameter, so it works like mkdir -p as well.

touch :

For those who like to “touch” to create a bunch of files, the cmdlet New-Item can do it when specifying File for the -ItemType parameter.
A nice usage example is :

 touch MyFile{1..4} 

This creates 4 files : Myfile1, Myfile2, Myfile3 and Myfile4.
Here is a simple way to do this (command + output) :

 1..4 | ForEach-Object { New-Item -ItemType file -Name "MyFile$_" }

    Directory: C:\Users\Administrator\Desktop

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         10/1/2015  10:06 AM          0 MyFile1
-a---         10/1/2015  10:06 AM          0 MyFile2
-a---         10/1/2015  10:06 AM          0 MyFile3
-a---         10/1/2015  10:06 AM          0 MyFile4

This deserves a few explanations.
“..” is the range operator, so “1..4” stands for 1,2,3,4.
ForEach-Object is a special cmdlet used for iteration. It executes the scriptblock between the {} once for every object passed to it via the pipeline. If you are wondering what is the $_ in the example above, it is a representation of the object currently being processed, which was passed from the pipeline. So, in the example above, $_ stores the value 1, then it stores the value 2, then the value 3 and finally the value 4.

cat :

Get-Content. But even when you run this cmdlet against a text file, this doesn’t output plain text, this outputs one object of the type string for each line in the file.
You can use its aliases : “cat”, “gc” or “type”.

tail :

 tail -n7 ./MyScriptFile.ps1 

This would output the last 7 lines of the file MyScriptFile.ps1

 Get-Content -Tail 7 .\MyScriptFile.ps1

The -Tail parameter has an alias : “-Last”, this makes this parameter more discoverable for those who “Tail” would not even cross their mind because they don’t have a Linux background.
This parameter was introduced with PowerShell 3.0.
An exceedingly valuable usage of the tail command for troubleshooting is tail -f to display any new lines of a log file as they are written to the file. For this, there is the -Wait parameter, which was introduced in PowerShell 3.0 as well.

grep :

This is the one I get asked about the most : “How do you do ‘grep’ in Powershell ?”, and my answer is : “Most of the time, you don’t.”
Think about what you are trying to achieve when you use grep : filtering lines of text which contain a specific value, string, or pattern. In Powershell, most of the time we are dealing with objects so what you want to achieve translates to : filtering the objects which have a specific value in one or more properties.
No text-parsing required here, unless you are dealing with objects of the type string.
Many cmdlets have built-in parameters which allows to filter the objects, but the generic filtering mechanism is the cmdlet Where-Object.
For example, to filter the processes which have a working set of more than 100 MB, you would run the following :

 Get-Process | Where-Object { $_.WorkingSet -gt 104857600 }

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    713      63   103340     130240   709    12.70   1604 powershell

Notice that in the output, the WorkingSet property is displayed in KiloBytes, but this is due to the formatting view, the actual value is in bytes.
Since PowerShell version 3.0, Where-Object supports a simplified syntax, so the following would do the same as the previous command :

 Get-Process | Where-Object WorkingSet -gt 104857600


By the way, in case you are dealing with strings and you really want to filter objects on a specific string or pattern, you can use Select-String.
One use case is working with a plain text log file, like the WindowsUpdate.log :

 Select-String -Path C:\Windows\WindowsUpdate.log -Pattern "Failed"

C:\Windows\WindowsUpdate.log:2264:2013-09-25    03:07:45:709     856    670    Agent    WARNING: Failed to
evaluate Installed rule, updateId = {818701AF-1182-45C2-BD1E-17068AD171D6}.101, hr = 80242013
C:\Windows\WindowsUpdate.log:2265:2013-09-25    03:07:49:157     856    670    Agent    WARNING: failed to
calculate prior restore point time with error 0x80070002; setting restore point
C:\Windows\WindowsUpdate.log:4692:2013-09-25    12:02:03:967     972    380    Misc    WARNING: WinHttp:
SendRequestToServerForFileInformation MakeRequest failed. error 0x8024402c
C:\Windows\WindowsUpdate.log:4693:2013-09-25    12:02:03:967     972    380    Misc    WARNING: WinHttp:
SendRequestToServerForFileInformation failed with 0x8024402c
C:\Windows\WindowsUpdate.log:4694:2013-09-25    12:02:03:967     972    380    Misc    WARNING: WinHttp:
ShouldFileBeDownloaded failed with 0x8024402c

And if you are a regular expression nerd, you can feed them to the -Pattern parameter.

uname :

For example, uname -a outputs the OS, the hostname, the kernel version and the architecture.
A good way to get the equivalent information on a Windows machine with PowerShell is to use the Win32_OperatingSystem class from CIM/WMI :

 Get-CimInstance Win32_OperatingSystem | Select-Object -Property Caption, CSName, Version, BuildType, OSArchitecture |
Format-Table -AutoSize

Caption                         CSName   Version  BuildType           OSArchitecture
-------                         ------   -------  ---------           --------------
Microsoft Windows 7 Enterprise  TOOLS-55 6.1.7601 Multiprocessor Free 64-bit

Yes, I know, it is much longer. Fortunately, there is tab completion for cmdlets, parameters, and even, sometimes, for parameter values.
The Format-Table -AutoSize is just to make the width of the output blog-friendly.

mkfs :

New-Volume or Format-Volume if the volume already exists.

ping :

Off course, you could run ping.exe from Powershell but if you want an object-oriented ping equivalent, there is the cmdlet Test-Connection :

 Test-Connection | Format-Table -AutoSize

Source   Destination  IPV4Address  IPV6Address Bytes Time(ms)
------   -----------  -----------  ----------- ----- --------
TOOLS-55             32    0
TOOLS-55             32    0
TOOLS-55             32    0
TOOLS-55             32    0

Here is what a ping failure looks like :
Test-Connection Failure

Notice the -Count parameter which allows to specify the number of echo request to send.

man :

Get-Help (or just “help”). The PowerShell help system is extremely… helpful and it would deserve an entire article on its own.
To get the help information for a specific cmdlet, let’s say Stop-Service :

 Get-Help Stop-Service -Full

The parameter -Full allows you to get everything : the description, the syntax, information on every parameter, usage examples…
You can also search help information on conceptual topics. Let’s say you are a regular expression nerd and you want to know how they work :

 Get-Help "about_regular*" 

The help files for conceptual topics have a name starting with “about_” .

cut :

This is used to select one or more columns from a text input (usually a file). Because this is plain text, you need to do a mapping from the fields you want to column numbers, and depending on the format of the input, you may need to specify a delimiter to define the columns. In PowerShell, this text-parsing stuff is very seldom required because again, we deal with objects. You want to retain only some properties from some input objects, just use Select-Object and specify the property names. That’s it.

 Get-ChildItem *.txt | Select-Object -Property "Name", "Length" |
Format-Table -AutoSize

Name         Length
----         ------
Lab.txt         160
modif.txt      1710
To watch.txt     97
wifi.txt        860

The double-quotes surrounding the property names are not mandatory. They indicate that these are string values but because the -Property parameter of Select-Object expects strings, PowerShell is kind enough to cast (convert) these values to strings if we forget the double-quotes.

There are plenty of other commands and I cannot write on all of them, but if there are popular commands that you think should be there, please let me know and I will add them to this article.

A warning on $ErrorActionPreference and Try/Catch for .NET developers

I recently stumbled upon a PowerShell Script from a .NET Developer/Architect who shall remain nameless, which contained this :

# set error action preference so errors don't stop and the trycatch 
# kicks in to handle gracefully
$ErrorActionPreference = "Continue" 
try {
catch {

Let me this say this bluntly : this is a HUGE misunderstanding of error handling in PowerShell.
Yes, PowerShell is based on the .NET Framework. Yes, it understands the different types of exceptions which are in the Framework class library. Yes, it uses Try/Catch or Try/Catch/Finally blocks to handle errors, like in C#, but there are differences.

In C#, when can choose to handle exceptions using Try/Catch or to not handle the exception directly in the current method and let the CLR look for a Catch block in another method somewhere up the call stack.

In PowerShell, we generally don’t use the term “exception”, we use the term “error” more often, but that’s mainly a terminology thing.

The big catch (pun intended) is that Powershell have terminating errors and non-terminating errors. A terminating error is raised by a cmdlet when something is wrong, so wrong that it cannot continue processing any other items in the pipeline and it has to stop immediately. Generally, this is because the cmdlet doesn’t know what the hell you are talking about ( a syntax or a type-related error).

A non-terminating error is raised by a cmdlet when something is wrong but it does not prevent the cmdlet from processing any other items. Here is a example of a non-terminating error :

Non-Terminating error

We can see that Get-ChildItem raises an error because it cannot find the file DoesNotExist.txt, but it keeps going and processes any other file names we specified.

The second part of the catch (pun intended again), and this is where many PowerShell beginners get tripped up, is that the Catch block will execute only if a cmdlet in the Try block raises a terminating error. So, the Get-ChildItem example above would not trigger the Catch block.

The solution : either you set $ErrorActionPreference to “Stop” or at the cmdlet level, you set the parameter -ErrorAction to “Stop” to convert any error from that cmdlet to a terminating error. Personally, I prefer the latter because I don’t like to mess with global variables from within my scripts.

terminating error

This time, Get-ChildItem stops immediately and doesn’t process any other file.
So, how do we correct the script to ensure that the Catch block will actually be executed when an error occurs in the Try block ?

Try {
    # cmdlets which might raise terminating or non-terminating errors
    Get-ChildItem DoesNotExist.txt, DoesExist.txt -ErrorAction "Stop"
Catch {
    # Error handling code

Here, any error raised by the cmdlet Get-ChildItem will stop execution and execute whatever error handling code we put in our Catch block.

So, my advice to developers coming from C# or any other language to PowerShell is : don’t try to guess PowerShell behaviours based on the behaviours of other languages. In fact, as an engineer, you should never make assumptions.

Remember, the root cause of any bug is that someone, somewhere, made an assumption.

Rebooting a server remotely

” Have you tried turning it off and on again? “

This quote from IT Crowd reflects probably the most universal stereotype about IT support. By the way, using this shortcut instead of actually diagnosing the problem shouldn’t be the usual course of action. But still, a reboot can really save you when you are in firefighting mode.

What if you need to reboot a machine which is remote or in a locked server room ? Here are a few commands to do that remotely:


The good old shutdown.exe is a command line executable, it can be called from a cmd.exe command prompt or from a Powershell console.
To reboot a remote machine called MySickServer, run the command :
shutdown -r -m \\MySickServer

You can also schedule the reboot for later. For example, the reboot MySickServer one hour from now, you can run :
shutdown -r -t 3600 -m \\MySickServer

Another nice usage of the shutdown command is : shutdown -i


It exposes pretty much all the options of the shutdown command in a nice little UI.


Conveniently, Powershell 3.0 introduced a cmdlet for this exact purpose : Restart-Computer.

There is a limitation to keep in mind, though : this cmdlet will fail if a user is currently logged into the remote server. In this case, if you are willing to kick any user out and force any application to close, may the -Force be with you :

Restart-Computer -ComputerName "MySickServer" -Force

At the other end of the safety spectrum, there is the -WhatIf parameter. It tells you what Restart-Computer would do on which computer(s), without actually doing it.

Restart-Computer -ComputerName "MySickServer" -Whatif

The WMI way

If your computer is still using Powershell 2.0, then you can rely on the good ol’ WMI. The WMI class Win32_OperatingSystem have the methods Shutdown, Win32Shutdown and Reboot. We are just going to look at the Reboot methods for our purpose here but you can refer to the following article for the other methods.

We get a WMI object for our remote server and store it into a variable. Notice that we can pass specific credentials for this remote server. The -Credential parameter can take an existing PSCredential object or we can use Get-Credential to get a prompt where we can enter a username and password.

$OS = Get-WmiObject Win32_OperatingSystem -ComputerName "MySickServer" -Credential (Get-Credential)

When we have the desired WMI object stored into our variable, we can call the Reboot method on the variable as seen above, using the dot notation.


This is a new cmdlet available only in Windows 8.1 (and later) and Windows Server 2012 R2 (and later) and it comes from the PcsvDevice Powershell module. PCSV stands for Physical Computer System View, if you were curious.

For its remoting protocol, it uses WS-MAN or IPMI, both of which are industry standards, meaning not proprietary , which is good for interoperability. The cmdlets in the PcsvDevice module are for out-of-band management, this means that they don’t even require an OS to be up and responding on the target server, they just require a Baseboard Management Controller (BMC).

 Restart-PcsvDevice -TargetAddress -Credential (Get-Credential) -ManagementProtocol IPMI 

Notice that we specify the “remoting” protocol (WS-MAN or in this case, IPMI). Actually, we have to specify it, it’s a mandatory parameter.
You can also set up a CIM session and then, use this session to connect to the remote server, like so :

 $Session = New-CimSession -ComputerName -Credential (Get-Credential)
Restart-PcsvDevice -CimSession $Session 

And this is just one cmdlet from the PcsvDevice module, there are others…

Getting the CPU time and status of threads in a process with Powershell

If there is a process hanging or consuming CPU resources and you have no idea why, a good place to start is to have a look at its threads and what they are doing.

Fortunately, Threads are a property of the object you get when run Get-Process :


Here, we just see an array of numbers, not very informative.

But these objects returned by Get-Process are very rich and their “Threads” property have their own set of properties and methods :


I cut the output for brevity, but there are a total of 16 properties for the ProcessThread objects, so we have a lot to play with !

Here are the most interesting properties to see the threads which are consuming CPU time, what kind of CPU time and, if they are waiting, what they are waiting for:


This is showing a lot of potential but the property values are not very human-readable.
So, I did some member-massaging (that sounds wrong) properties customization and addition, to get the times in a more usable format : percentage and seconds.
I ended up with a function, which takes one or more process IDs from a parameter called ID :

#Requires -Version 2
function Get-ProcessThreadsInfo {
    $Processes = Get-Process -Id $ID
    Foreach ($Process in $Processes) {
        # Displaying the process name
        Write-Output $Process.ProcessName.ToUpper()
        Write-Output "`r"
        # Displaying of threads and the number of threads consuming CPU time in this process
        $NumberOfThreads = ($Process | Select-Object -ExpandProperty Threads | Measure-Object).count
        $NumberofThreadsWithCPUTime = ($Process | Select-Object -ExpandProperty Threads |
        Where-Object { $_.TotalProcessorTime.Ticks -ne 0 } | Measure-Object).count

        Write-Output "Number of threads in this process : $NumberOfThreads "
        Write-Output "Number of threads consuming CPU time in this process : $NumberofThreadsWithCPUTime "
        # Extracting the threads objects from each process
        $ProcessThreads = $Process | Select-Object -ExpandProperty Threads | Where { $_.TotalProcessorTime.Ticks -ne 0 }
        Foreach ($ProcessThread in $ProcessThreads) {
            # Building custom properties for my threads objects
            $ThreadID = @{L="ThreadID";E={ $ProcessThread.Id }}
            $CPUTimeProperty = @{L="CPU Time (Sec)";E={ [math]::round($ProcessThread.TotalProcessorTime.TotalSeconds,2) }}
            $UserCPUTimeProperty = @{ L="User CPU Time (%)";E={ [math]::round((($ProcessThread.UserProcessorTime.ticks / $ProcessThread.TotalProcessorTime.ticks)*100),1) }}
            $SystemCPUTimeProperty = @{L="System CPU Time (%)";E={ [math]::round((($ProcessThread.privilegedProcessorTime.ticks / $ProcessThread.TotalProcessorTime.ticks)*100),1) }}
            $State = @{L="State";E={ $ProcessThread.ThreadState }}
            # Mapping the possible values of WaitReason to their actual meaning (source: https://msdn.microsoft.com/en-us/library/tkhtkxxy(v=vs.110).aspx)
            switch ($ProcessThread.WaitReason) {
                EventPairHigh { $Wait_ReasonPropertyValue =
                "Waiting for event pair high.Event pairs are used to communicate with protected subsystems." ; break }
                EventPairLow { $Wait_ReasonPropertyValue =
                "Waiting for event pair low. Event pairs are used to communicate with protected subsystems." ; break }
                ExecutionDelay { $Wait_ReasonPropertyValue =
                "Thread execution is delayed." ; break }
                Executive { $Wait_ReasonPropertyValue =
                "The thread is waiting for the scheduler." ; break }
                FreePage { $Wait_ReasonPropertyValue =
                "Waiting for a free virtual memory page." ; break }
                LpcReceive { $Wait_ReasonPropertyValue =
                "Waiting for a local procedure call to arrive."; break }
                LpcReply { $Wait_ReasonPropertyValue =
                "Waiting for reply to a local procedure call to arrive." ; break }
                PageIn { $Wait_ReasonPropertyValue =
                "Waiting for a virtual memory page to arrive in memory." ; break }
                PageOut { $Wait_ReasonPropertyValue =
                "Waiting for a virtual memory page to be written to disk." ; break }
                Suspended { $Wait_ReasonPropertyValue =
                "Thread execution is suspended." ; break }
                SystemAllocation { $Wait_ReasonPropertyValue =
                "Waiting for a memory allocation for its stack." ; break }
                Unknown { $Wait_ReasonPropertyValue =
                "Waiting for an unknown reason." ; break }
                UserRequest { $Wait_ReasonPropertyValue =
                "The thread is waiting for a user request." ; break }
                VirtualMemory { $Wait_ReasonPropertyValue =
                "Waiting for the system to allocate virtual memory." ; break }
                Default { $Wait_ReasonPropertyValue = " " ; break }
            # Creating the custom output object
            $ProcessThread | Select-Object -Property $ThreadID, StartTime, $CPUTimeProperty, $UserCPUTimeProperty, $SystemCPUTimeProperty, $State |
            Add-Member -NotePropertyName "Wait Reason" -NotePropertyValue $Wait_ReasonPropertyValue -PassThru 

The output looks like this :


This is not exhaustive or deep information by any means, but it can be a starting point to drill down further with other tools.

As you can see, Internet Explorer (iexplore) is waiting for me, so… gotta go.