Merge branch 'ShellCustomizations' into 'main'
Shell Customizations See merge request d_mcknight/blog-content!5
This commit is contained in:
commit
88ba8bdf53
1 changed files with 151 additions and 0 deletions
151
2024-03-10_Shell-Customizations.md
Normal file
151
2024-03-10_Shell-Customizations.md
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
---
|
||||||
|
date: 2024-03-27
|
||||||
|
title: Shell Customizations and SSH
|
||||||
|
tags:
|
||||||
|
- homelab
|
||||||
|
- linux
|
||||||
|
- bash
|
||||||
|
- development
|
||||||
|
---
|
||||||
|
|
||||||
|
Taking a detour from actual deployments, I recently started experimenting with different shell customizations.
|
||||||
|
Until now, I've just used whatever default shell I have available on a given system, but a recent live stream
|
||||||
|
from [Lawrence Systems](https://www.youtube.com/@LAWRENCESYSTEMS) mentioned shell customizations and tmux and
|
||||||
|
lead me down this rabbithole. I'll also mention SSH configuration as its something that greatly helps my daily
|
||||||
|
productivity and is tangentially related to some of my customizations.
|
||||||
|
|
||||||
|
## tmux
|
||||||
|
I only recently tried using tmux and I don't know why I went so long without it! The
|
||||||
|
[tmux wiki](https://github.com/tmux/tmux/wiki) offers a complete explanation of what tmux is and all of the
|
||||||
|
things it can do; in short, it manages processes and terminals. `tmux` has a pretty steep learning curve, but
|
||||||
|
it only took me a day to get the hang of a few basic shortcuts; `ctrl`+`b`, or the "prefix key" enables
|
||||||
|
interacting with `tmux` and then a command can be issued. Some of the commands I find most useful are:
|
||||||
|
- `?` - Shows a list of tmux key bindings
|
||||||
|
- `"` - Splits the window vertically and adds a pane
|
||||||
|
- `up`/`down` - Navigates between vertical panes; if you hold `ctrl`+`b` then it moves the split up and down
|
||||||
|
- `d` - Detaches the temux session; easy to remember since `ctrl`+`d` is how you detach an SSH session
|
||||||
|
|
||||||
|
I'm not (yet) using tmux by default for local sessions, but I do have it enabled to run when I SSH into some
|
||||||
|
systems; this is the primary reason I really like `tmux`. I can ssh into my server from my laptop and start a
|
||||||
|
command (i.e.a very long rsync process) without worrying about disowning the process before my laptop goes
|
||||||
|
to sleep; when I ssh back in, perhaps from another computer, I have the same terminal history and the same
|
||||||
|
process attached. I will go into more detail in the "ssh" section of this post, but all I had to do is run
|
||||||
|
`tmux new -A -s ssh_tmux` when I connect to attach a tmux session named `ssh_tmux`, creating it if it doesn't
|
||||||
|
exist.
|
||||||
|
|
||||||
|
## ssh
|
||||||
|
For SSH keys, I have one key pair I use for personal systems and another for work. I won't go into detail about
|
||||||
|
key management or key rotation and instead will focus on SSH configuration. My SSH config simply contains global
|
||||||
|
config options and includes other configuration files for specific hosts. I find this makes it easier to manage
|
||||||
|
hosts since I can group hosts in different files. My `~/.ssh/config` looks like:
|
||||||
|
```
|
||||||
|
Include config.d/*
|
||||||
|
Host *
|
||||||
|
AddKeysToAgent yes
|
||||||
|
IdentitiesOnly yes
|
||||||
|
```
|
||||||
|
- `AddKeysToAgent` adds keys to my ssh agent so I don't need to enter a passcode after the first time I use a key
|
||||||
|
- `IdentitiesOnly` prevents SSH from trying to infer an identity file to use if it isn't specified in SSH config
|
||||||
|
or explicitly supplied as an argument.
|
||||||
|
|
||||||
|
> You can find descriptions of all the SSH config options in the [BSD manpages](https://man.openbsd.org/ssh_config).
|
||||||
|
|
||||||
|
A couple example hosts look like:
|
||||||
|
```
|
||||||
|
Host work.server
|
||||||
|
HostName <server IP Address>
|
||||||
|
User <remote username
|
||||||
|
IdentityFile ~/.ssh/id_rsa_work
|
||||||
|
RemoteCommand tmux new -A -s ssh_tmux
|
||||||
|
RequestTTY yes
|
||||||
|
...
|
||||||
|
Host mcknight.unraid
|
||||||
|
HostName 192.168.1.100
|
||||||
|
User root
|
||||||
|
IdentityFile ~/.ssh/id_rsa_home
|
||||||
|
RemoteCommand tmux new -A -s ssh_tmux
|
||||||
|
```
|
||||||
|
- `RemoteCommand` is executed on the remote system upon SSH connection. This can cause some odd quirks since the
|
||||||
|
command is executed immediately upon connection. I only ran into trouble if I needed to change a password upon
|
||||||
|
connection (unlikely situation if you're connecting with SSH keys) and with rsync (more on that below). This
|
||||||
|
can be overridden by connecting with `ssh mcknight.unraid -o RemoteCommand=None`.
|
||||||
|
- `RequestTTY` I found was required on one Ubuntu Server in order for tmux to start properly. I don't know exactly
|
||||||
|
why this is necessary, but it solved a problem.
|
||||||
|
|
||||||
|
|
||||||
|
To work around rsync issues, I aliased the command in my `.bashrc`: `alias rsync="rsync -e 'ssh -o RemoteCommand=none'"`.
|
||||||
|
This might cause some problems with fully local transfers, but I don't alias this on servers where I would move
|
||||||
|
enough data around to necessitate using rsync.
|
||||||
|
|
||||||
|
## BASH Configuration
|
||||||
|
I use `bash` primarily since its available in pretty much every Linux distro, Windows, and MacOS (not that I use
|
||||||
|
MacOS). I mostly use a standard `.bashrc` that ships with Mint, but I recently updated the PS1 and PS2 variables
|
||||||
|
using [Tom from Lawrence Systems'](https://github.com/lawrencesystems/dotfiles/blob/master/.bash_profile) as
|
||||||
|
inspiration.
|
||||||
|
|
||||||
|
My shell prompt looks like:
|
||||||
|
<pre><font color="#9E7199">┌──[</font><font color="#06989A">22:27</font><font color="#9E7199">]─[</font><font color="#06989A">d_mcknight</font><font color="#9E7199">@</font><font color="#06989A">MCKNIGHT-FW13</font><font color="#9E7199">]─(</font><font color="#1D6CBF">~</font><font color="#9E7199">)</font>
|
||||||
|
<font color="#9E7199">└</font><font color="#06989A">$</font>
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
And a snippet from .bashrc related to that:
|
||||||
|
```shell
|
||||||
|
color_off="\[\033[0m\]" # Text Reset
|
||||||
|
|
||||||
|
# Regular Colors
|
||||||
|
black="\[\033[0;30m\]"
|
||||||
|
red="\[\033[0;31m\]"
|
||||||
|
green="\[\033[0;32m\]"
|
||||||
|
yellow="\[\033[0;33m\]"
|
||||||
|
blue="\[\033[0;34m\]"
|
||||||
|
purple="\[\033[0;35m\]"
|
||||||
|
cyan="\[\033[0;36m\]"
|
||||||
|
white="\[\033[0;37m\]"
|
||||||
|
|
||||||
|
path_color=$blue
|
||||||
|
chrome_color=$purple
|
||||||
|
context_color=$cyan
|
||||||
|
prompt_symbol=@ # 🚀💲
|
||||||
|
prompt='\$'
|
||||||
|
if [ "$EUID" -eq 0 ]; then # Change prompt colors for root user
|
||||||
|
context_color=$red
|
||||||
|
prompt_symbol=💀
|
||||||
|
fi
|
||||||
|
PROMPT_COMMAND='if [[ $? != 0 && $? != 130 ]];then echo "⚠️";fi'
|
||||||
|
PS1="$chrome_color┌──[${context_color}\A${chrome_color}]─"'${debian_chroot:+('${path_color}'$debian_chroot'${chrome_color}')─}${VIRTUAL_ENV:+('${path_color}'$(realpath $VIRTUAL_ENV --relative-to $PWD --relative-base /home)'${chrome_color}')─}'"[${context_color}\u${chrome_color}${prompt_symbol}${context_color}\h${chrome_color}]─(${path_color}\w${chrome_color})\n${chrome_color}└${context_color}${prompt}${color_off} "
|
||||||
|
PS2=$chrome_color'└>$color_off '
|
||||||
|
|
||||||
|
export VIRTUAL_ENV_DISABLE_PROMPT=1
|
||||||
|
```
|
||||||
|
There's a lot going on in the prompt, but I will highlight pieces of it; keep in mind that the snippets below may
|
||||||
|
not be valid on their own and that some of the color formatting may be lost as I cut up the long prompt string.
|
||||||
|
|
||||||
|
- `PROMPT_COMMAND='if [[ $? != 0 && $? != 130 ]];then echo "⚠️";fi'` isn't part of the prompt string, but rather an
|
||||||
|
expression evaluated before the prompt string. This renders a ⚠️ if the previous command fails and isn't just an
|
||||||
|
empty command.
|
||||||
|
|
||||||
|
- `[${context_color}\A${chrome_color}]` prints the current time in 24H format. Helpful if you open a terminal and
|
||||||
|
want to quickly reference when you ran the last command and when it completed.
|
||||||
|
|
||||||
|
- `${debian_chroot:+('${path_color}'$debian_chroot'${chrome_color}')─}`
|
||||||
|
and `${VIRTUAL_ENV:+('${path_color}'$(realpath $VIRTUAL_ENV --relative-to $PWD --relative-base /home'${chrome_color}')─})`
|
||||||
|
print an active chroot or Python venv path, if active. I print the venv path relative to the current directory
|
||||||
|
if within `home`; I find this helpful when I have multiple projects to make sure I know which one I'm looking at.
|
||||||
|
Note that I also specify `export VIRTUAL_ENV_DISABLE_PROMPT=1` to suppress the default `(venv)` prompt prefix.
|
||||||
|
|
||||||
|
- `[${context_color}\u${chrome_color}${prompt_symbol}${context_color}\h${chrome_color}]` looks like the default shell,
|
||||||
|
`user@hostname` except when root the `@` is replaced with `💀` as an extra visual reminder that its a root shell.
|
||||||
|
|
||||||
|
- `(${path_color}\w${chrome_color})` shows the current shell path like the default shell
|
||||||
|
|
||||||
|
- `\n${chrome_color}└${context_color}${prompt}${color_off} ` continues to the next line and inserts the `$` or `#`
|
||||||
|
prompt, followed by a space.
|
||||||
|
|
||||||
|
- `PS2=$chrome_color'└>$color_off '` updates the prefix used when there's a newline in a command. I like that this
|
||||||
|
makes each line start vertically aligned and it keeps the chrome colored differently from the inputs.
|
||||||
|
|
||||||
|
## Further Reading
|
||||||
|
SSH, BASH, and tmux are all over a decade old with plenty of good documentation and write-ups that I've referenced.
|
||||||
|
My current `.bashrc` is in [this gist](https://gist.github.com/d-mcknight/176899ca924b5b4cfdf7692e36ca568e) and
|
||||||
|
I will try to keep that updated as I make changes; maybe one day I'll promote it to an actual repository with other
|
||||||
|
config files.
|
Loading…
Reference in a new issue