Shell: a handful of invaluable pointers

2020-08-17 @Technology

For anyone new to the Unix shell, take note of the pointers I here provide. It’s difficult to believe that I managed to operate for years without some of these invaluable gems, applicable to procedures carried out all the time, saving significant typing and redundancy.

Without further ado, I commence with what I deem most important and proceed onward.

  1. <Ctrl>-R shortcut.

    Normally as you execute shell commands, UP and DOWN lets you cycle through the command history. But this sequential history cycling is slow to reproduce older commands.

    Rather, hit CTRL-R and type a letter or substring pertaining to any portion of any previously executed command or argument. The last matched command will appear. Hit CTRL-R again to continue to older matchings, and so forth. Or you can delete the search string via backspace and enter another.

    Shell would prove torturous if not for this otherwise pervasive shortcut.

  2. VI mode.

    Likewise, I can’t conceive of Shell without the VI mode enabled.

    set -o vi enables it, which, if using the Bash shell, you should plop into your $HOME/.bashrc first thing tomorrow. The benefits are multiple:

    • Normal mode/insert mode separation, per VIM. i enters the insert mode, ESC and CTRL-[ returns into normal mode.

    • A large subset of VIM shortcuts enabled: j/k to cycle through the history (forget the distant UP/DOWN keys), w/W for word jumpings, dw/dW for word deletions, f/F, c/C, u (undo), and many, many more, including the famous mnemonic combinations.

    • v in normal mode opens your command in VIM for editing. If the shell VI mode was insufficient, you’ve now the freedom to manipulate your command to your wildest desire in your favourite editor.

    Note: for the Emacs users, there is the respective mode that I know nothing of other than that set -o emacs enables it. This should satisfy both camps.

  3. Modify the Shell prompt from the default.

    Rather than the simple uninformative $ to precede all commands, modify the prompt to display user@host:current_path$:

    PS1="\u@\h:\w\$ "
    

    Alternatively, the color version, per my current setting:

    PS1="\[\033[00;32m\]\u@\h\[\033[00m\]:\[\033[01;36m\]\w\[\033[00m\]\$ "
    
  4. {,} set operators. Examples best demonstrate:

    $ mv -v dir1/dir2/file{1,2}.txt 
    renamed 'dir1/dir2/file1.txt' -> 'dir1/dir2/file2.txt'
    
    $ mv -v {,prefix_}file
    renamed 'file' -> 'prefix_file'
    
    # Copy of a file from one host to another (via localhost):
    $ scp -3 {host1,host2}:/path/file
    
    $ echo string{1,2{a..d}}
    string1 string2a string2b string2c string2d
    
    $ mkdir -pv top/mid{1,2}/bottom{1..3}
    mkdir: created directory 'top'
    mkdir: created directory 'top/mid1'
    mkdir: created directory 'top/mid1/bottom1'
    mkdir: created directory 'top/mid1/bottom2'
    mkdir: created directory 'top/mid1/bottom3'
    mkdir: created directory 'top/mid2'
    mkdir: created directory 'top/mid2/bottom1'
    mkdir: created directory 'top/mid2/bottom2'
    mkdir: created directory 'top/mid2/bottom3'
    
    # Rsync the local top/sub with the remote:~/top/sub
    $ rsync -avu {,remote:~/}top/sub 
    
  5. shopt -s autocd.

    Add this to your shell config as well ($HOME/.bashrc). You can then switch directories without typing cd.

    In the examples below, the typed commands follow $ with the output after.

    <some dir>$ ~
    cd -- /home/user
    
    ~$ mkdir -pv dir1/dir2/dir3
    mkdir: created directory 'dir1'
    mkdir: created directory 'dir1/dir2'
    mkdir: created directory 'dir1/dir2/dir3'
    
    ~$ dir1/dir2/dir3
    cd -- dir1/dir2/dir3
    
    ~/dir1/dir2/dir3$ ../../..
    cd -- ../../..
    
    ~$
    
  6. ^ replace operator. Occasionally quicker for small updates to the immediately prior command:

    $ echo "what a dreadful day"
    what a dreadful day
    
    $ ^day^sentence
    echo "what a dreadful sentence"
    what a dreadful sentence
    
    $ cp -v file{1,2}
    'file1' -> 'file2'
    
    $ ^2^3
    cp -v file{1,3}
    'file1' -> 'file3'
    
    $ ^3^4
    cp -v file{1,4}
    'file1' -> 'file4'
    

Questions, comments? Connect.