25 December 2019

Fast and effective work in command line

*nixShells
Original author: Vasily Laur

There are a lot of command line tips and trics in the internet. Most of them discribe the trivials like "learn the hotkeys" or "sudo !! will run previous command with sudo". Instead of that, I will tell you what to do when you have already learned the hotkeys and know about sudo !!.


The terminal should start instantly


How much time you spend to launch a terminal? And another one? For a long time I've used Ctrl+Alt+T shortcut to launch a terminal and I thought it is fast. When I've migrated from Openbox to i3, I began to launch a terminal via Win+Enter, that binding worked out of the box. You know what? Now I don't think that Ctrl+Alt+T is fast enough.


Of course, the thing is not a millisecond speedup, but that you open a terminal at the level of reflexes, completely oblivious to that.


So, if you often use a terminal, but grab a mouse for launching it, try to configure a handy hotkey. I'm sure, you will like it.


Zsh instead of Bash


This is a holywar topic, I know. You should install Zsh for at least three features: advanced autocompletition, typo correction and multiple pathname completition: when a single Tab converts /u/s/d into /usr/share/doc. Arch Linux has already migrated to Zsh in it's installation CD. I hope Zsh will once become a default shell in Ubuntu. That will be a historical moment.


Starting to use Zsh is not difficult at all. Just install it via package manager and find a pretty config. I recommend to take config used in Arch Linux:


$ wget -O ~/.zshrc https://git.grml.org/f/grml-etc-core/etc/zsh/zshrc

The only thing left is to change your default shell and relogin.


$ chsh -s $(which zsh)

Thats all, just keep working like nothing happened.


How the shell prompt should look like


Shell prompt is a small piece of text shown in the terminal at the beginning of your command line. It should be configured for your kind of work. You can perceive it as a dashbord of a vehicle. Please, put some useful information there, let it help you navigate! Make it handy especially if you see it every day!


Shell prompt should be colored. Do not agree? Try to count how many commands were executed in this terminal:



And now with color:



Shell prompt should display a current working directory of a shell. If current working directory is not displayed, you have to keep it in mind and periodically check it with pwd command. Please don't do that. Keep in mind some really important things and don't waste your time for pwd command.


If you sometimes switch to root account, you need a "current user" indication. The particular user name is often not important, but it's status (regular or root) is. The solution is to use color: red shell prompt for root and green for regular user. And you will never take over root shell as regular.


If you connect to servers using ssh, you need to distinguish your local and remote shells. For that purpose your shell prompt should contain a hostname, or even better — indicate an ssh connection.


Shell prompt can show the exit code of the last command. Remember that zero exit code means a command exited successfully, non-zero — command exited unsuccessfully. You can obtain last command's exit code via echo $?, but typing all that is a damn long thing. Let the shell show you unsuccessfull exit instead.


If you work with Git repos, it will be useful to see the repository status in shell prompt: current branch and the state of working directory. You will save some time on git status and git branch commands and won't commit to a wrong branch. Yes, the calculation of status may take significant time in fat repositories, but for me the pros outweight the cons.


Some people add clock to the shell prompt or even the name of a virtual terminal (tty), or some arbitrary squiggles. That's all superfluous. It's better to keep much room for commands.


Thats how my shell prompt looks like in different conditions:



You can see on the screenshot that the terminal titlebar does the similar job. It's also a piece of a dashboard and it's also should be configured.


So, how all this stuff should be implemented in .zshrc? The PROMPT variable sets the left prompt and RPROMPT sets the right prompt. The EUID variable defines the status of a user (regular or root) and SSH_CLIENT or SSH2_CLIENT presence indicates ssh connection. So we can have a template:


if [[ -n "$SSH_CLIENT" || -n "$SSH2_CLIENT" ]]; then
  if [[ $EUID == 0 ]]; then
    PROMPT=...
  else
    PROMPT=...
  fi

else # not SSH
  if [[ $EUID == 0 ]]; then
    PROMPT=...
  else
    PROMPT=...
  fi
fi

I don't show the copy-paste-ready code since the exact implementation is a matter of taste. If you don't want to bother and the screenshot above is ok for you, than take my cofig from the Github.


Summary:


  • Colored shell prompt is a must have.
  • The required minimum is a current working directory.
  • Root shell should be clearly visible.
  • The name of a user don't care a payload if you use only one account.
  • The hostname is useful if you connect to servers via ssh, it's not mandatory if don't.
  • It's useful to see unsuccessful exit code of a last command.
  • Git repo status saves time on git status and git branch commands and brings foolproof.

Heavily use the command history


The most part of commands in your life you enter more than once, so it would be cool to pull them out from the history instead of typing again. All modern shells save a command history and provide several ways of searching through that history.


Perhaps you are already able to dig the history using Ctrl+R keybinding. Unfortunately it has two disadvantages:


  1. The command line should be empty to begin the search, i.e. in case "one began to type a command — remembered about the search" you have to clean out your typing first, then press Ctrl+R and repeat your input. That takes too long.
  2. Forward search don't work by defaul since Ctrl+S stops the terminal.

The most fast and convenient type of search works this way:


  1. You begin to type a command,
  2. you remember about the search,
  3. you press a hotkey and the shell offers you commands from history that started the same way.

For example you want to sync a local directory with a remote one using Rsync and you already did it two hours earlier. You type rsync, press a hotkey one or two times and the desired command is ready to be launched. You don't need to turn on the search mode first, the shell prompt don't change to (reverse-i-search)':, and nothing jumps anywhere. You're just scrolling through history the same way you press the arrows ↑↓ to scroll through previously entered commands but with additional filtering. That's damn cool and saves a lot of time.


This kind of search don't work by default in Bash and Zsh, so you have to enable it manually. I have chosen PgUp for searching forward and PgDown for searching backward. It's far to reach them, but I've alredy made a habit. Maybe later I will switch to something closer like Ctrl+P and Ctrl+N.


For Bash you need to add a couple of strings to /etc/inputrc of ~/.inputrc:


"\e[5~": history-search-backward
"\e[6~": history-search-forward

If you have taken a foreign complete .zshrc, it's highly probable that PgUp and PgDown already do the job. If not, then add to ~/.zshrc:


bindkey "^[[5~" history-beginning-search-backward # pg up
bindkey "^[[6~" history-beginning-search-forward  # pg down

Fish and Ipython shells already have such a search binded to arrows ↑↓. I think that many users migrated to Fish just for the arrows behavior. Of course, it is possible to bind the arrows this way in both Bash and Zsh if you wish. Use this in /etc/inputrc of ~/.inputrc:


"\e[A":history-search-backward
"\e[B":history-search-forward

And this for in ~/.zshrc:


autoload -U up-line-or-beginning-search
autoload -U down-line-or-beginning-search
zle -N up-line-or-beginning-search
zle -N down-line-or-beginning-search
bindkey "^[[A" up-line-or-beginning-search
bindkey "^[[B" down-line-or-beginning-search

It's curious that over time I began to write commands bearing in mind that later I will pull them out from history. Let me show you some techniques.


Join the commands that always follow each other:


# ip link set eth1 up && dhclient eth1
# mkdir /tmp/t && mount /dev/sdb1 /tmp/t

Absolute paths instead of relative let you run a command from any directory:
vim ~/.ssh/config instead of vim .ssh/config, systemd-nspawn /home/chroot/stretch instead of systemd-nspawn stretch and so on.


Wildcard usage makes your commands more universal. I usually use it in conjunction with chmod and chown.


#  chown root:root /var/www/*.sq && chmod 644 /var/www/*.sq

Keyboard shortcuts


Here is the required minimum.


Alt+. — substitutes the last argument of the previous command. It's also may be accessed with !$.
Ctrl+A, Ctrl+E — jumps to the beginning and the end of the line respectively.
Ctrl+U, Ctrl+Y — cut and paste. It's handy when you type a complex command and notice that you need to execute another one first. Hmm, where to save the current input? Right here.
Ctrl+W — kills one word before the cursor. It clears out the line when being pressed and hold. By default the input is saved to the clipboard (used for Ctrl+Y).
Ctrl+K — cuts the part of the line after the cursor, adding it to the clipboard. Ctrl+A Ctrl+K quickly clears out the line.
PgUp, PgDown, Ctrl+R — history search.
Ctrl+L clears terminal.


Keyboard responsiveness


I want to show you a small setup that allows you to scroll, navigate and erase faster. What do we do when we want to erase something big? We press and hold Backspace and watch it runs back wiping characters. What is going on exactly? After Backspace is pressed, one character disappears, then goes a small delay, then autorepeat is triggered: Backspace erases characters one by one, like you hit it repeatedly.


I recommend you to adjust the delay and autorepeat frequency for the speed of your fingers. The delay is required when you want to erase only one character — it gives you the time to release a key. Too big delay makes you wait for an autorepeat. Not enough for you to be annoyed, but enough to slow down the transfer of your thoughts from the head to the computer. The bigger the autorepeat frequency is, the faster the text is being erased and the more difficult is to stop this process. The goal is to find an optimum value.


So, the magic command is:


$ xset r rate 190 20

190 — delay duration in milliseconds,
20 — frequency in repeats per second.


I recommend to start from these values and increase the delay bit by bit until false positives, then return a little. If the delay is too small, you won't be able to use the keyboard. To fix this an X-server or complete computer should be restarted. So, please be carefull.


In order to save parameters you need to add this command somewhere in X autostart.


Process exit indication


I often have to start some long-running processes: a fat backup, big data transfer, archive packing/extracting, package building an so on. Usually I start such a process, switch to another task and gaze occasionally if my long-runnig process has exited. Sometimes I dive too deep into work and forget about it. The solution is to add process exit notification that will take me out of trance.


There are many tools for that purpose: notify-send, dzen2, beep, aplay, wall. All of them are good somehow, but don't work with ssh connection. That's why I use terminal beep:


$ long-running-command; echo $'\a'

ASCII encoding has 0x7 character, named bell. It is used to beep the PC speaker. PC-speaker is not a modern thing, not every computer has it and it's not heared in headphones. That's why some terminals use a so called visual bell. I use urxvt, and it performs visual bell by raising urgency flag. What is it? It's a thing used when a window want to tell you it is urgent.


You can check how your terminal reacts on bell character right now:


$ sleep 3; echo $'\a'

Three seconds are given for you to switch to another window, it may be required.


Unfortunately, not every terminal can display visual bell by raising urgency flag. I've checked the most popular.


Terminal emulator visual bell as urgency flag
konsole may be enabled in preferences
urxvt yes
xfce4-terminal may be enabled in preferences
xterm no
cool-retro-term no
lxterminal no
gnome-terminal no

It's too long to type echo $'\a', so I've made a wake alias.


Aliases


By default commands cp, scp and rm work non-recursively and that sucks! It's a damn bad legacy! Well, it may be fixed using aliases. But first let's look when non-recursive behavior can be useful.


$ mkdir foodir
$ cp * foodir

Only files will be copied into foodir, but not directories. The same situation goes with rm:


$ rm *

will delete only files and symlinks, but keep directories. But how often do you need this feature? I like to think that cp and rm always work recursively.


Ok, but what about security? Maybe non-recursive behavior protects your files? There is one case when you have a symlink to the directory and you want to remove that symlink, but keep the directory. If a slash is appended (intentionally or occasionaly) to the directory name and the recursive mode is switched on via -r, the directory will become empty! EMPTY!


$ ln -s foodir dir_link
$ rm -r dir_link/

Without -r arg it will abuse and don't remove anything. So, recursive rm increases the risk of loosing data a little.


I turned on the recursive mode for cp, scp и rm, and also added -p for mkdir to create nested directories easily.


alias cp='cp -r'
alias scp='scp -r'
alias rm='rm -r'
alias mkdir='mkdir -p'

For two years I've never regretted about these aliases and never lost data. There is also a downside: it's possible to copy/remove less data, than it was needed and not get sight of it when working on the system without aliases. So, please be careful. I know what I do and always run rm with caution.


The most popular are ls aliases and you probably already use them:


alias ls='ls -F --color=auto'
alias la='ls -A'
alias ll='ls -lh'
alias lla='ll -A'

Also a colored grep is much more pretty than colorless:


alias grep='grep --colour=auto'

Aliases don't work in scripts, don't forget that fact! You have to explicitly specify all arguments.


Touch typing


It's obvious, but I remind you: touch typing helps to type faster. It will be hard in the beginning, but you'll overcome the limits over time.


The best time to learn touch typing is vacation, when nobody bothers you. Please don't hurry when learning! Your goal is to memorize where each character is located, not so much with your mind, but with your fingers. It's better to type slow, but without mistakes rather than fast with mistakes. Remember that masters have good results not by fast fingers but not doing mistakes.


Don't forget to take a break. Your brain and fingers need to take a rest. When mistakes begin to appear, that means you need to take a break.


That is all for today


I hope these tips will really help you. Good luck!

Tags:linuxbashshellzshterminal
Hubs: *nix Shells
0
1.2k 7
Comments 2
Top of the last 24 hours