Published in Articles on by Michiel van Oosterhout ~ 4 min read

The prompt of a command-line shell is a good place to display some contextual information, for example the current working directory or the status of the current Git repository. Now that we have Commando for Bash we can start creating some extensions that can be used to add such contextual information to the Bash prompt.

The Bash prompt

Bash gives a lot of control over its prompt. Bash will execute every command in the PROMPT_COMMAND array before displaying the prompt, and will display the value of the PS1 variable as the primary prompt (and the value of PS2 as the secondary prompt). Certain character sequences, when found in these prompt variables, will be replaced, for example \H with the hostname or \u with the username. Parameter expansion and command substitution are also performed on the prompt variables prior to display (unless the promptvars option is disabled).

Commando extensions for a custom Bash prompt

We are going to create some Commando extensions that each add a command to the PROMPT_COMMAND array to set a variable holding some contextual information. Such variables can then be used in the PS1 prompt variable. We'll start with some information about the current Git repository.

The GitAt extension

First we'll create a Commando extension to declare a function that can give the information we need. The Windows PowerShell script below creates a new Commando extension called GitAt.

# Ensure the directory exists
$path = "$Env:LOCALAPPDATA\Commando\GitAt"
$_ = New-Item $path -Force -ItemType Directory

# Declare the Bash script contents
$script = @'
function git_at {
    # Check that current directory is in a Git repository
    if git rev-parse --is-inside-work-tree > /dev/null 2>&1; then
        # Get current branch
        local result=$(git branch --show-current)

        # Get first tag instead
        if [ -z "$result" ]; then
            result=$(git --no-pager tag --points-at HEAD | head -n 1)
            if [ ! -z "$result" ]; then
                result="[$result]"
            fi
        fi

        # Get commit ID instead
        if [ -z "$result" ]; then
            result=$(git rev-parse --short HEAD)
        fi

        # Echo result
        if [ ! -z "$result" ]; then
            echo $result
        fi
    fi
}
'@

# Create the Bash script
$path = "$path\git_at.bash"
Set-Content -Path $path -Value $script

# Ensure proper line endings
((Get-Content $path) -join "`n") + "`n" | Set-Content -NoNewline $path

The extension exports the git_at function, which checks if the current directory is in a Git repository, and, if so, echos the name of the current branch, or the name of the current tag when not on a branch, or the current commit ID.

The GitAt.Prompt extension

We can create another extension that will use the git_at function to set PROMPT_GIT_AT every time a prompt is rendered, via an addition to the PROMPT_COMMAND array.

# Ensure the directory exists
$path = "$Env:LOCALAPPDATA\Commando\GitAt.Prompt"
$_ = New-Item $path -Force -ItemType Directory

# Declare the Bash script contents
$script = @'
PROMPT_COMMAND+=('PROMPT_GIT_AT=$(git_at)')
'@

# Create the Bash script
$path = "$path\Extend.bash"
Set-Content -Path $path -Value $script

# Ensure proper line endings
((Get-Content $path) -join "`n") + "`n" | Set-Content -NoNewline $path

With this extension in place, the PROMPT_GIT_AT variable is always available for use in the prompt variable.

The Dir.Prompt extension

Because we also want to display the current working directory in the prompt, we'll create one more extension. This one will use the dirs command to set PROMPT_DIR every time a prompt is rendered, again via an addition to the PROMPT_COMMAND array.

# Ensure the directory exists
$path = "$Env:LOCALAPPDATA\Commando\Dir.Prompt"
$_ = New-Item $path -Force -ItemType Directory

# Declare the Bash script contents
$script = @'
PROMPT_COMMAND+=('PROMPT_DIR=$(dirs +0)')
'@

# Create the Bash script
$path = "$path\Extend.bash"
Set-Content -Path $path -Value $script

# Ensure proper line endings
((Get-Content $path) -join "`n") + "`n" | Set-Content -NoNewline $path

With this extension in place, the PROMPT_DIR variable is always available for use in the prompt variable.

Now we can use the prompt function (see Prompt mockups in Git Bash) to set a PS1 prompt variable: PS1='\n$(prompt bW "$PROMPT_DIR" / Y- "$PROMPT_GIT_AT") '.

Configuring the Bash prompt with the current Git branch
Configuring the Bash prompt with the current Git branch

Setting PS1 automatically

The last remaining piece of the puzzle is to set the PS1 variable automatically. For this we'll add a file to interactive.d:

# Ensure the directory exists
$path = "$Env:USERPROFILE\.config\bash\interactive.d"
$_ = New-Item $path -Force -ItemType Directory

# Declare the Bash script contents
$script = @'
PS1='\n$(prompt bW "$PROMPT_DIR" / Y- "$PROMPT_GIT_AT") '
'@

# Create the Bash script
$path = "$path\prompt.bash"
Set-Content -Path $path -Value $script

# Ensure proper line endings
((Get-Content $path) -join "`n") + "`n" | Set-Content -NoNewline $path

With this script in place, the Bash prompt is automatically configured at the beginning of any interactive session.

The newly configured Bash prompt
The newly configured Bash prompt

Conclusion

By building small pieces one at a time, we were able to build a larger puzzle. We started with the color and prompt functions. Then we set up a configuration system for Bash, which we then used to implement our own extension system, for which we then wrote some extensions. Configuring the prompt was then a simple one-line Bash script.