Shell Customizations
This commit is contained in:
		
							parent
							
								
									925d3f58ee
								
							
						
					
					
						commit
						1b9b31999f
					
				
					 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue