Skip to content

Commit

Permalink
feat: chapter 'customise your command prompt' (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
dwmkerr authored Jun 7, 2021
1 parent 05042c5 commit f068d22
Show file tree
Hide file tree
Showing 7 changed files with 852 additions and 19 deletions.
229 changes: 229 additions & 0 deletions effective-shell-playground/scripts/set_ps1.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
# Note: this script should be sourced into your shell. For this reason it does
# not specify a shebang.
#
# set-ps1: set the command prompt (PS1) variable to a named 'theme'.
#
# example:
#
# set-ps1 simple
#
# If no 'theme' is provided, the original PS1 value that was set in the shell
# startup files is used.

# Keep track of the original PS1 value.
_original_ps1="${PS1}"

set_ps1() {
# Foreground colours.
local fg_black=$(tput setaf 0) # \033[30m
local fg_red=$(tput setaf 1) # \033[31m
local fg_green=$(tput setaf 2) # \033[32m
local fg_yellow=$(tput setaf 3) # \033[33m
local fg_blue=$(tput setaf 4) # \033[34m
local fg_magenta=$(tput setaf 5) # \033[35m
local fg_cyan=$(tput setaf 6) # \033[36m
local fg_white=$(tput setaf 7) # \033[37m

# Background colours.
local bg_black=$(tput setab 0) # \033[40m
local bg_red=$(tput setab 1) # \033[41m
local bg_green=$(tput setab 2) # \033[42m
local bg_yellow=$(tput setab 3) # \033[43m
local bg_blue=$(tput setab 4) # \033[44m
local bg_magenta=$(tput setab 5) # \033[45m
local bg_cyan=$(tput setab 6) # \033[46m
local bg_white=$(tput setab 7) # \033[47m

# Text styles and reset. Note that on some terminals 'bold' will produce
# light colours for bright colours, on others it will actually show the text
# in bold.
local bold=$(tput bold) # \033[1m
local dim=$(tput dim) # \033[2m
local start_underline=$(tput smul) # \033[4m
local stop_underline=$(tput rmul) # \033[24m
local reset=$(tput sgr0) # \033[0m

# If you need them, you can get other terminal capabilties such as the
# number of colours, lines, columns, etc.
local colors=$(tput colors) # Number of colours, e.g. '8' or '256'
local lines=$(tput lines) # Number of lines in the terminal
local cols=$(tput cols) # Number of columns in the terminal

# Special characters for PS1 strings:
# \a an ASCII bell character (07)
# \d the date in "Weekday Month Date" format (e.g., "Tue May 26")
# \D{format}
# the format is passed to strftime(3) and the result is inserted into the prompt string; an empty format results in a locale-specific time representation. The
# braces are required
# \e an ASCII escape character (033)
# \h the hostname up to the first `.'
# \H the hostname
# \j the number of jobs currently managed by the shell
# \l the basename of the shell's terminal device name
# \n newline
# \r carriage return
# \s the name of the shell, the basename of $0 (the portion following the final slash)
# \t the current time in 24-hour HH:MM:SS format
# \T the current time in 12-hour HH:MM:SS format
# \@ the current time in 12-hour am/pm format
# \A the current time in 24-hour HH:MM format
# \u the username of the current user
# \v the version of bash (e.g., 2.00)
# \V the release of bash, version + patch level (e.g., 2.00.0)
# \w the current working directory, with $HOME abbreviated with a tilde (uses the value of the PROMPT_DIRTRIM variable)
# \W the basename of the current working directory, with $HOME abbreviated with a tilde
# \! the history number of this command
# \# the command number of this command
# \$ if the effective UID is 0, a #, otherwise a $
# \nnn the character corresponding to the octal number nnn
# \\ a backslash
# \[ begin a sequence of non-printing characters, which could be used to embed a terminal control sequence into the prompt
# \] end a sequence of non-printing characters

# Depending on the name of the theme provided, set the prompt.
case $1 in
basic)
# Debian/Ubuntu style:
# \u@\h - username@host
# \w - working directory
# \$ - prompt
PS1="\u@\h:\w\\$ "
;;

debian)
# Debian/Ubuntu style:
# \u@\h - username@host (bold/green)
# \w - working directory (bold/blue)
# \$ - prompt (# if root, otherwise $) (bold/white)
PS1="\[${bold}${fg_green}\]\u@\h:\[${fg_blue}\]\w\[${fg_white}\]\\$\[${reset}\] "
;;

datetime)
# A style that shows the date and time:
# \D{%Y-%m-%d} - the year/month/date (in white)
# \@ - the time (in green)
# \$ - prompt (# if root, otherwise $) (bold/white)
PS1="\[${fg_white}\]\D{%Y-%m-%d} \[${bold}${fg_green}\]\@\[${fg_white}\] \\$\[${reset}\] "
;;

git)
# A Debian style prompt with the git info above it.
PS1="\$(_git_info)"$'\n'"\\[${bold}${fg_green}\]\u@\h:\[${fg_blue}\]\w\[${fg_white}\]\\$\[${reset}\] "
;;

dwmkerr)
# The current folder, up to 3 items shown, the git info, then the prompt.
# Starts with a leading newline to space out commands.
PS1=$'\n'"\[${bold}${fg_blue}\$(_pwd_max_folders 3)${reset}\] \$(_git_info)"$'\n'"\[${bold}${fg_white}\]\\$\[${reset}\] "
;;


# Add your own themes here!

*)
# Restore PS1 to its original value.
PS1="${_original_ps1}"
;;

esac

# If we are in Z-Shell convert the PS1 to use Z-Shell format.
[ -n "$ZSH_VERSION" ] && PS1=$(_to_zsh "$PS1")
}

# Build a string that shows:
# - The branch (underlined if 'main') in green
# - A red exclamation if there are any local changes not committed
# - An indicator of the number of stashed items, if any.
_git_info() {
# Don't write anything if we're not in a folder tracked by git.
if ! [ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" == "true" ]
then
return
fi

# Local copies of the colours for this function.
local fg_red=$(tput setaf 1)
local fg_green=$(tput setaf 2)
local fg_yellow=$(tput setaf 3)
local start_underline=$(tput smul)
local stop_underline=$(tput rmul)
local reset=$(tput sgr0)
# Git details.
local git_branch_name="$(git branch --show-current)"
local git_any_local_changes="$(git status --porcelain=v1 2>/dev/null)"
local git_stash_count="$(git rev-list --walk-reflogs --count \
refs/stash -- 2>/dev/null)" # Ignore error when no stashes
local git_info=""
if [ "${git_branch_name}" = "main" ]; then
git_info="${fg_green}${start_underline}${git_branch_name}${reset}"
else
git_info="${fg_green}${git_branch_name}${reset}"
fi
if ! [ -z "${git_any_local_changes}" ]; then
# Note that we have to be careful to put the exclamation mark
# in single quotes so that it is not expanded to the last command!
git_info="${git_info} ${fg_red}"'!'"${reset}"
fi
if [ "${git_stash_count:-0}" -gt 0 ]; then
git_info="${git_info} ${fg_yellow}${git_stash_count} in stash${reset}"
fi
printf "${git_info}"
}

# Show the pwd, limited to a certain number of folders.
_pwd_max_folders() {
local max_folders="$1"
# Write the PWD, replace the home path with a tilde, then cut out the last
# of the directories, up to the max folders value.
echo "${PWD/#$HOME/'~'}" | rev | cut -d'/' -f1-${max_folders} | rev
}

# Convert a Bash PS1 string to a ZSH PS1 string.
_to_zsh() {
# Note that the following sequences are not supported as they don't have
# an Z-Shell equivalent. Mappings have been worked out from this document:
# https://zsh.sourceforge.io/Doc/Release/Prompt-Expansion.html#Prompt-Expansion
#
# \l the basename of the shell's terminal device name
# \s the name of the shell, the basename of $0 (the portion following the final slash)
# \T the current time in 12-hour HH:MM:SS format
# \v the version of bash (e.g., 2.00)
# \V the release of bash, version + patch level (e.g., 2.00.0)
# \W the basename of the current working directory, with $HOME abbreviated with a tilde
# \# the command number of this command

# Remove the non-printing characters sequences as Z-Shell doesn't need them.
# Then replace Bash special characters with Z-Shell special characters.
# Before we echo out the PS1 value, we need to replace '\u' with '%n'. If we
# don't do this here with shell parameter expansion, then 'echo' thinks '\u'
# is the beginning of a unicode character. This took quite a while to work
# out. I have left the replacement of '\u' to '%n' in the 'sed' call below
# but it not needed as the shell expansion below handles it.
# Note that before we replace '\$' with '%#', we replace '\$(' temporarily
# to SHELL_EXPAND, otherwise things like '\$(_my_func)' would be transformed
# to '%#(_my_func)', i.e. we need to replace the prompt special character
# but not escaped shell functions.
local zsh_ps1="$(echo ${1/\\u/%n} | sed \
-e 's/\\\[//g' \
-e 's/\\\]//g' \
-e 's/\\\d/%w/g' \
-e 's/\\\D/%D/g' \
-e 's/\\\u/%n/g' \
-e 's/\\\h/%m/g' \
-e 's/\\\H/%M/g' \
-e 's/\\\j/%j/g' \
-e 's/\\\t/%*/g' \
-e 's/\\\A/%T/g' \
-e 's/\\\@/%@/g' \
-e 's/\\\w/%~/g' \
-e 's/\\\!/%!/g' \
-e 's/\\\$(/SHELL_EXPAND/g' \
-e 's/\\\$/%#/g' \
-e 's/SHELL_EXPAND/\\\$(/g' \
)"

# Print the sequence. Note that we need to escape the % symbol.
local zsh_ps1_escaped=$(echo "${zsh_ps1}" | sed 's/%/%%/g')
printf "${zsh_ps1_escaped}"
}
13 changes: 5 additions & 8 deletions structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ This document contains the proposed structure of the book. It is still work in p
* [Interlude - The Shell Family Tree](#interlude---the-shell-family-tree)
* [Part 4 - Building Your Toolkit](#part-4---building-your-toolkit)
* [Chapter 25 - Configuring the Shell](#chapter-25---configuring-the-shell)
* [Chapter 26 - Customising your Command Prompt](#chapter-26---customising-your-command-prompt)
* [Chapter 26 - Customising Your Command Prompt](#chapter-26---customising-your-command-prompt)
* [Chapter 27 - Managing Your Dotfiles](#chapter-27---managing-your-dotfiles)
* [Chapter 21 - Getting to Grips with Git](#chapter-21---getting-to-grips-with-git)
* [Chapter 24 - How to avoid scripting!](#chapter-24---how-to-avoid-scripting)
Expand Down Expand Up @@ -76,7 +76,6 @@ This document contains the proposed structure of the book. It is still work in p

<!-- vim-markdown-toc -->

There are a number of different ways to configure your shell, and some options which can change how it operates. In this chapter we'll take a look at the different configuration files for the shell and how they work, and how you can change your shell configuration. We'll also see some of the shell options available which can change how the shell works.
# The Pitch

This is _not_ a book about Shell Programming. There are many excellent Shell Programming books (see below). This is about general terminal and shell skills which will make you effective with many day to day tasks. Some shell programming is covered, but this is primarily a book about how to work more effectively with command line interfaces and shells in general.
Expand All @@ -91,7 +90,6 @@ This is _not_ a book about Shell Programming. There are many excellent Shell Pro
- **Effective Shell**: 30 ways to use shells and terminals to be more effective.
- **Command the Shell**: 30 ways to be more effective with software.


# Foreword by Steve Bourne

For some, the text based interface to a computer might seem archaic in this day and age. However, the skills required to work with a computer shell might have never been in such high demand, or so relevant. In the introduction we cover just why the shell is more and more relevant for professionals and hobbyists, and why investing in your time learning about this topic could be a really smart move.
Expand All @@ -106,7 +104,6 @@ We'll also talk about why we are emphasising how things work on Linux, rather th

This is not a book about Bash. It's not a book about shell programming. It's a book about how to use a keyboard as the main way to work with a computer, and become incredibly efficient doing so. That means we'll cover a lot of Bash and shell topics, but we'll also look at how linux works. But this is not a book about systems adminstration. We won't see how to set up a mailserver, but we will understand _why_ linux systems work as they do, in a more fundamental way, which will help us save time and optimise our work, whether we're programming, administering systems, exploring or hobbying, working with data science or even just doing general purpose computing.


# Chapter 1 - Getting Started

Here we'll look at setting up your shell, how to open it, and how to configure your computer to follow along with the examples.
Expand Down Expand Up @@ -257,12 +254,10 @@ The goal of this part of the book is to equip you with the knowledge and techniq

There are a number of different ways to configure your shell, and some options which can change how it operates. In this chapter we'll take a look at the different configuration files for the shell and how they work, and how you can change your shell configuration. We'll also see some of the shell options available which can change how the shell works.

## Chapter 26 - Customising your Command Prompt
## Chapter 26 - Customising Your Command Prompt

The shell command prompt can be configured to show you what you find most important. In this section we'll see how the command prompt can be configured, and take a look at some of the advanced options available.

- configuring things like the default editor

## Chapter 27 - Managing Your Dotfiles

As you customise your shell and environment, it becomes more and more important to manage this customisation effectively and track changes to it. In this chapter we'll see how to manage your configuration - and 'dotfiles' - as a GitHub repository. We'll also see how this can be used to share ideas and look at some great examples.
Expand Down Expand Up @@ -419,8 +414,9 @@ This section contains the things which have been pulled out of chapters as they
- introduction: Note that we are going to use `#` to indicate comments
- introduction: Note that we are going to use `...` to indicate cropped output
- chapter: useful tools: fzf, ag, ack, ripgrep?
- todo: cronjobs, maybe as part of shell scripts?
- structure: the `sed` chapter is too big, let's extract all of the regular expressions descriptions to its own chapter
- todo: advanced topic - supporting bash auto-completion. The `set-ps1` function would be a good one to use as an example
- todo: wildcards is not sufficiently covered in chapter 2 or chapter 3, perhaps we need a short dedicated chapter on it? Also, what is the manpage for wildcards (e.g. what is the equivalent of `man re_pattern` (bash and zsh)
- todo: getting help - what is the `zsh` equivalent of `help`?
- `find` was in 'managing files
Expand Down Expand Up @@ -490,6 +486,7 @@ This section contains the things which have been pulled out of chapters as they
- todo: Nice date tricks: https://linux.101hacks.com/date-manipulation/past-date-and-time/
- todo: `lsof` for open ports https://linux.101hacks.com/monitoring-performance/lsof-command-examples/
- todo: excellent reference for common shell operations: https://devhints.io/bash
- todo: dotfiles: could be nice to show how you could put a shell script as a static page on your dotfiles repo to allow you to curl it in one go
# More Useful Reading
Expand Down
14 changes: 14 additions & 0 deletions style-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This style guide may be useful when proofing, writing or reviewing changes.
* [Common Mistakes](#common-mistakes)
* [Hyphenations](#hyphenations)
* [Info](#info)
* [Downloading the Samples](#downloading-the-samples)

<!-- vim-markdown-toc -->

Expand Down Expand Up @@ -73,3 +74,16 @@ This is documented at:

https://themes.gohugo.io//theme/hugo-book/docs/shortcodes/hints/

## Downloading the Samples

As a quick reference, the snippet below can be used to show the 'downloading the samples' instructions:

{{< hint info >}}
**Downloading the Samples**
Run the following commands in your shell to download the samples:

```sh
curl effective.sh | sh
```
{{< /hint >}}

Loading

0 comments on commit f068d22

Please sign in to comment.