Tag Archives: $env:COMPUTERNAME

Linux Prompt on Windows – Part I

I already have an answer to the question, “Why?” Because, I can. At some point in the last few days, I decided I would attempt to replicate the Linux prompt in PowerShell. So I did it, and I thought I would share it. I’ve broken this up a little with the full function toward the bottom of this evening’s post.

In this first section, we’ve created the prompt function’s basic structure and added our first bit of code. This code determines if the user that started the Windows PowerShell session is an administrator or not. If they are, they get the # symbol as part of the prompt, and if they’re not, they get the $ symbol.

Function Prompt {
    If ([bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).Groups -match 'S-1-5-32-544')) {
        $Symbol = '#'
    } Else {
        $Symbol = '$'
    }
}

The next addition includes the logic to determine how to write the path inside the prompt. If the current path is C:\Users\<CurrentUser>, it enters a tilde (~). If the path includes the C:\Users\<CurrentUser> path, in addition to one or more directories, it will enter a tilde and the other directory or directories, such as ~/Desktop, or ~/Favorites/Links. In the last case, it’ll simply list the current path after replacing the backslashes with forward slashes and dumping the drive letter and colon. In all instances, in fact, all (Windows) backslashes, will be replaced with (Unix/Linux) forward slashes.

Function Prompt {
    If ([bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).Groups -match 'S-1-5-32-544')) {
        $Symbol = '#'
    } Else {
        $Symbol = '$'
    }

    If ($PWD.Path -eq $env:USERPROFILE) {
        $Location = '~'
    } ElseIf ($PWD.Path -like "*$env:USERPROFILE*") {
        $Location = $PWD.Path -replace ($env:USERPROFILE -replace '\\','\\'),'~' -replace '\\','/'
    } Else {
        $Location = "$(($PWD.Path -replace '\\','/' -split ':')[-1])"
    }
}

The last section is where the prompt function actually writes the prompt to the screen. It’s the simple combination of the current user, an @ character, the computer name, the path, and the proper symbol (# or $). As you may notice, I’ve written the code to force the case of the current user and computer to lowercase.

Function Prompt {
    If ([bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).Groups -match 'S-1-5-32-544')) {
        $Symbol = '#'
    } Else {
        $Symbol = '$'
    }

    If ($PWD.Path -eq $env:USERPROFILE) {
        $Location = '~'
    } ElseIf ($PWD.Path -like "*$env:USERPROFILE*") {
        $Location = $PWD.Path -replace ($env:USERPROFILE -replace '\\','\\'),'~' -replace '\\','/'
    } Else {
        $Location = "$(($PWD.Path -replace '\\','/' -split ':')[-1])"
    }

    "$($env:USERNAME.ToLower())@$($env:COMPUTERNAME.ToLower()) $Location $Symbol "
}

In closing, I’ve included a gif of the prompt and an example of moving though the directory structure. While I typically only use full cmdlet names in my posts, this example does include aliases for the Set-Location cmdlet (cd, sl, and even chdir). Due to this function being saved in my profile script, this prompt displays immediately when a new console is opened, and every time after that. As someone that’s written a few different prompt functions, this one’s a keeper. I already can’t imaging switching back. On that note, you might not want to test it out.

duplicate-the-linux-prompt01

tommymaynard@srv01 / # $PWD.Path
C:\
tommymaynard@srv01 / # cd C:\Windows\
tommymaynard@srv01 /Windows # cd .\System32\
tommymaynard@srv01 /Windows/System32 # chdir ..
tommymaynard@srv01 /Windows # chdir ..
tommymaynard@srv01 / # sl /users
tommymaynard@srv01 /users # sl /users/tommymaynard2 # Not my current user.
tommymaynard@srv01 /users/tommymaynard2 # cd ..
tommymaynard@srv01 /users # cd C:\Users\tommymaynard\ # My current user.
tommymaynard@srv01 ~ # cd .\Desktop\
tommymaynard@srv01 ~/Desktop # cd ..
tommymaynard@srv01 ~ # cd .\Favorites\Links\
tommymaynard@srv01 ~/Favorites/Links # sl /
tommymaynard@srv01 / # cd /users
tommymaynard@srv01 /users # chdir \
tommymaynard@srv01 / #

Update1: A day later and I still love my new PowerShell prompt. I’ve made a minor change, however. The last line in the function is now the following three lines. It stores the prompt in a variable called $Prompt, modifies the title of the window to the $Prompt value, and then writes the prompt. In the past my window’s title indicate the computer name. Now it indicates that information, as well as the current user, the location on the current drive, and whether or not I’m running the session as an administrator.

...
	$Prompt = "$($env:USERNAME.ToLower())@$($env:COMPUTERNAME.ToLower()) $Location $Symbol "
	$Host.UI.RawUI.WindowTitle = $Prompt
	$Prompt
}

Update2: It occurred to me today that by entering the tilde character, such as Set-Location -Path ~, it moves you to the location stored in the Home property of the FileSystem PSProvider. Because of this, I opted to add another line, just before the beginning of the prompt function. Setting this property to $env:USERPROFILE, allows me to quickly move to C:\Users\<CurrentUser>. I should note that this property is sometimes already set. In those cases, this is just a precaution that the ~ character will move you back home.

(Get-PSProvider -PSProvider FileSystem).Home = $env:USERPROFILE
Function Prompt {
...
}

In case you want to take this prompt function for a spin, and you do, here’s everything. Copy and paste it to your console and hit Enter a couple times and try it out. If you hate it, close and reopen the PowerShell console and it never existed. Cheers.

(Get-PSProvider -PSProvider FileSystem).Home = $env:USERPROFILE
Function Prompt {
    If ([bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).Groups -match 'S-1-5-32-544')) {
        $Symbol = '#'
    } Else {
        $Symbol = '$'
    }
 
    If ($PWD.Path -eq $env:USERPROFILE) {
        $Location = '~'
    } ElseIf ($PWD.Path -like "*$env:USERPROFILE*") {
        $Location = $PWD.Path -replace ($env:USERPROFILE -replace '\\','\\'),'~' -replace '\\','/'
    } Else {
        $Location = "$(($PWD.Path -replace '\\','/' -split ':')[-1])"
    }

	$Prompt = "$($env:USERNAME.ToLower())@$($env:COMPUTERNAME.ToLower()) $Location $Symbol "
	$Host.UI.RawUI.WindowTitle = $Prompt
	$Prompt
}

 

Add a Dynamic Number of Asterisks

Recently, on a Windows PowerShell forum, a person wanted a script to run each time they connected to a PowerShell remote endpoint — such as a local profile does when you open the PowerShell, console host. I recommended a couple options: Create a new endpoint, and use a ScriptsToProcess .ps1 file, or modify the default, Microsoft.PowerShell endpoint, using the Set-PSSessionConfiguration cmdlet.

To use the second option I recommended, you need to add a start up script, such as the example below does. Keep in mind that you can name the .ps1 file anything you’d like. That said, I typically name these files so that I’m certain what they go with — such as, which endpoint.

PS> Set-PSSessionConfiguration -Name Microsoft.PowerShell -StartUpScript C:\Microsoft.PowerShell.StartUpScript.ps1

In order to test my updated, Microsoft.PowerShell endpoint (on my desktop computer), I decided I would add a couple informational lines of text to the script. One would indicate the “remote” computer’s name, and the other would provide the date and time. Here’s that output.

PS> Enter-PSSession -ComputerName . # This dot indicates the local computer
You are connected to TOMMYSCOMPUTER.
6/11/2015 21:17:15 PM
[localhost]: PS C:\>

As I stared at this, I decided I would prefer that the date and time was centered under the first line, with asterisks on either side, so that both lines were the same length. This means I would need to dynamically determine how many asterisks I would need on each side of my date and time, so that the asterisks, and the date and time text, would fill up as much space as the line above. It was a new PowerShell challenge, and so I ran with it.

Before we go further, let me start by showing you an image of the end results, in case the explanation wasn’t clear. Then, we’ll walk though what I did to accomplish this task.

Add-a-Dynamic-Number-of-Asterisks-2015-01

I’ve broken down my start up script into five different parts. As you’ll see, I did things very procedurally. I wasn’t worried about using a minimal amount of commands and variables. For those with more experience, you’ll easily see how things could have been much tidier. Let’s start with the first two lines.

$Computer = "You are connected to $env:COMPUTERNAME."
$Date = Get-Date -Format G

These two lines set one variable each: $Computer and $Date. Each of these values will be used in the next example.

$TopLength = $Computer.Length
$BottomLength = $Date.Length

This section, above, determines and stores the character length of the $Computer and $Date variables. We are going to need to add asterisks for the difference between these two lengths.

$DiffInLength = $TopLength - $BottomLength
$Before = [math]::Round($DiffInLength/2)
$After = $DiffInLength - $Before

This third section puts the difference between $TopLength and $BottomLength into a variable called $DiffInLength. It then creates a $Before variable to store 1/2 of $DiffInLength — it will round up, if this amount isn’t a whole number. It then creates an $After variable, which will hold the leftovers from the previous calculation.

$Before = '*' * $Before
$After = '*' * $After

Next, as seen above, we’ll overwrite our $Before and $After variables with new values. Each of those values will be a set of asterisks. How do we know how many? We multiply the asterisk symbol (a string character) by the numeric values stored in the $Before and $After variables.

Write-Host -Object $Computer -ForegroundColor Green
Write-Host -Object $Before$Date$After -ForegroundColor Green

If you’re still following along, we then write out our two lines. The first line is made up of the $Computer variable. Following that line, we write our first set of asterisks, the date, and then the second set of asterisks.

This might have been a lot to take in, so you may consider copying and pasting each line into your PowerShell host and watch as it creates the two lines for you. Regardless of the length of your computer name, your two lines will be the same length, too.

Now, that last statement isn’t always true. What if we changed $Computer to just be the computer name, such as $Computer = $env:COMPUTERNAME? I know what happens; it’s throws errors and won’t let you connect to the endpoint. This problem begins with us subtracting a larger number from a smaller number. I’ve added some logic to skip a good portion of our script, if the $TopLength is smaller than $BottomLength. I’ve include the full script below.

$Computer = "You are connected to $env:COMPUTERNAME."
$Date = Get-Date -Format G

$TopLength = $Computer.Length
$BottomLength = $Date.Length

If ($TopLength -gt $BottomLength) {
    $DiffInLength = $TopLength - $BottomLength
    $Before = [math]::Round($DiffInLength/2)
    $After = $DiffInLength - $Before
    $Before = '*' * $Before
    $After = '*' * $After
}

Write-Host -Object $Computer -ForegroundColor Green
Write-Host -Object $Before$Date$After -ForegroundColor Green

In case someone wants to a see a tidier version of this same script, then here it is. We still use the $Before and $After variables, but no others, outside of our $Computer and $Date variables.

$Computer = "You are connected to $env:COMPUTERNAME."
$Date = Get-Date -Format G

If ($Computer.Length -gt $Date.Length) {
    $Before = '*' * ([math]::Round(($Computer.Length - $Date.Length)/2))
    $After = '*' * $Before.Length
}

Write-Host -Object $Computer -ForegroundColor Green
Write-Host -Object $Before$Date$After -ForegroundColor Green