Command line tips

Fish shell

The Fish shell is a shell with very sane defaults that can do a lot out of the box and makes you feel at home right after installing it.

Beware though, some not-so-commonly-used features of bash do not exist in the fish shell and it isn’t compatible with POSIX sh. Where it really shines is as an interactive shell.

The tutorial gives a quick and great overview of its capabilities.

Getting help

When in doubt

  • man cat
  • help cd
  • jq --help, jq -h
  • apropos "list dir", man -k "list dir" search the manual

Moving around

  • cd - go to the last visited directory
  • dirs print the dirs stack
  • pushd . add current directory to the dirs stack
  • popd pop and go to the last one

Working with files

  • > my-file overwrites the file’s content
  • >> my-file appends to the file’s existing content
  • < my-file inputs the file’s content, same as cat my-file | but without starting an additional process
  • mkdir my-new-directory make a new directory
  • diff <(ls) <(ls -a), <() treats the output of the command as a file (doesn’t work in fish)

All the following commands support -v for verbose and -i for interactive

  • mv my-file new-file moves a file

  • cp my-file new-file copies a file

  • rm my-file, rm -r my-directory remove a file or directory

  • file.conf{,.old} will expand to file.conf file.conf.old

    • cp file.conf{,.old} create a backup file
    • mv file.conf{.old,} revert to the backup
    • convert file{.jpg,.png}

Combining CLI tools

Pipes !

  • seq 100 | grep 3 | wc -l counts in how many numbers “3” appears between 1 and 100

Tee !

  • seq 100 | tee 100.txt splits the output between stdout and the argument

Interacting with the clipboard

  • cat file.txt | xclip -selection clipboard copy text
  • xclip -selection clipboard -o > file.txt paste text

Creating custom tools

Binary executable

Make them in a compiled language such as Rust, Go, C…

Interpreted scripts

Use the first line of the file to describe how to run it:

#!/usr/bin/env python3

def factorial(n):
    res = 1
    for i in range(2, n + 1):
        res *= i

    return res

if __name__ == "__main__":
    import sys
    n = int(sys.argv[1])
./ 5

Shell functions

factorial() { echo 1; seq $1 | paste -s -d\* | bc; }
factorial 5

Can be put in config file to be reusable.


alias la='ls -1a --group-directories-first'

Can be put in config file to be reusable.

Useful bits

  • wget -mkEpnp make an offline mirror of a site, short version of
    --mirror --convert-links --adjust-extension --page-requisites --no-parent
  • while read in; do echo "$in"; done < file.txt, repeat a command for every line of a file (doesn’t work in fish, but can be used with sh -c '...')
  • tar -zcC /my/source/path my-folder > ~/my-backup-$(date +%s).tar.gz make a quick backup

Juggle with the CLI


Most shells (fish, bash, zsh…) support two modes: the vi mode and the emacs mode with the mappings of each editor. Shells are in emacs mode by default.

Vi bindings

Normal and insert mode, just like vim with nearly all of its bindings. The Vi mode is especially nice in the Fish shell.

  • fish_vi_key_bindings fish
  • set -o vi bash & co

Emacs bindings

  • <C-/> undo (doesn’t work in Fish)
  • <C-a> go to the beginning of the line
  • <C-e> go to the end of the line
  • <C-k> delete from cursor to the end of the command line
  • <C-u> delete from cursor to the beginning of the command line
  • <C-r> triggers history search
  • <C-w> delete from cursor to beginning of the word
  • <C-y> paste word or text that was cut using one of the deletion shortcuts (such as the one above) after the cursor
  • <C-x-x> move between start of command line and current cursor position (and back again)
  • <Alt>b move backward one word (or go to start of word the cursor is currently on)
  • <Alt>f move forward one word (or go to end of word the cursor is currently on)
  • <Alt>d delete to end of word starting at cursor (whole word if cursor is at the beginning of word)


  • commands starting with a space don’t go in the history
  • <Alt>.or <Esc>. adds the last command’s last word
  • <C-r> search the history backwards
  • <C-g> escape from history searching mode
  • <C-p> previous command in history (i.e. walk back through the command history)
  • <C-n> next command in history (i.e. walk forward through the command history)


  • <C-l> clear the screen
  • <C-s> stops the output to the screen (for long running verbose command)
  • <C-q> allow output to the screen (if previously stopped using command above)
  • <C-c> terminate the command
  • <C-z> suspend/stop the command use bg to run it in background, jobs to list background jobs and fg to bring them back
  • & after a command to run it in background
  • nohup before a command to detach it’s life cyle from the current terminal
  • &>/dev/null redirects the output of the process to /dev/null, use &>/dev/null/ & after a command to ignore its output and run it in background
  • nohup command &>/dev/null & hence creates a totally independent process, command </dev/null &>/dev/null & achieves the same goal by instantly sending EOF to the program (using this you won’t see the Done after the backgrounded process ends)

Bang ! (doesn’t work in fish)

  • !! run last command, sudo !! to add a forgotten sudo.
  • !blah run the most recent command that starts with “blah” (e.g. !ls)
  • !blah:p print out the command that !blah would run (also adds it as the latest command in the command history)
  • !$ the last word of the previous command (same as Alt + .)
  • !$:p print out the word that !$ would substitute
  • !* the previous command except for the first word (e.g. find a b gives a b)
  • !*:p print out what !* would substitute

Substitutions (doesn’t work in fish')

  • ^foo^bar same as !!:s/foo/bar/, replace foo with bar in the last command and run it
  • Go up in history and hit <C-o>, this will execute the picked command and write the following one in the prompt