More Useful Utilities

So far, we have a small but powerful list of commands:

  • cd, ls, mv, touch, rm, mkdir, rmdir, and pwd for information and basic manipulation of the file system
  • which for finding the location of programs
  • env and export for environment variables and setting new variables
  • echo for printing values to the screen, such as variables after expansion

In this chapter, we'll look at a variety of other commands that you'll likely encounter as you use the terminal more in your development journey.

Command Manuals with man

Since I want this book to stay a reasonable length, I only briefly introduce the commands here. But, as the saying goes, "if you teach a developer one pattern for a command they can use that one pattern, but if you teach a developer to check the manual, they can use all the patterns". Maybe I just made that up, actually.

For many system commands, you can use the man command (short for "manual") to pull up a fancy interactive manual:

[stephen@virtualbox ~] man man
MAN(1)                                                                                       Manual pager utils                                                                                       MAN(1)

NAME
       man - an interface to the system reference manuals

SYNOPSIS
       man [man options] [[section] page ...] ...
       man -k [apropos options] regexp ...
       man -K [man options] [section] term ...
       man -f [whatis options] page ...
       man -l [man options] file ...
       man -w|-W [man options] page ...

DESCRIPTION
       man is the system's manual pager.  Each page argument given to man is normally the name of a program, utility or function.  The manual page associated with each of these arguments is then found and
       displayed.  A section, if provided, will direct man to look only in that section of the manual.  The default action is to search in all of the available sections following a pre-defined order  (see
       DEFAULTS), and to show only the first page found, even if page exists in several sections.

The manual pages are built in to most Linux systems for all the common commands and many more uncommon commands too. They contain command descriptions, usage examples, documentation of options, and pretty much all the information you might need for most uses of each command.

Navigating the manual pages with the up and down arrow keys is possible, but slow. Instead, man allows you to use the same key bindings as less, a useful program used for viewing files and long outputs. To summarize:

  • d moves "down" one-half page, while b moves "back" one half-page
  • u moves "up", but by one full page instead of half-page
  • / allows you to search for specific text. For example, if I wanted to know what the -i option for man does, I can type: /-i and press Enter, it will highlight the matches and jump the view to the first match
    • As an example: Searching man pages
  • If you have searched for text with /, you can use n to jump to the next match and N to jump to the previous match
  • q will quit the program
  • g by itself will move to the top of the file, or if you provide a number and then type g, you'll move to the line number provided
  • G by itself moves to the bottom of the file, or with a number does the same as g

Alternative Naming with alias

Sometimes, you may want to give a command you use frequently a "shortcut" command to save time. You can use the alias command to do so:

~
bsh ❯ alias me='echo "Hello there, $(whoami)!"'

~
bsh ❯ me
Hello there, stephen!

Nifty!

To set these every time you open your shell, you can just put alias commands in your .bashrc file. Although you shouldn't overuse aliases, a light sprinkling of them can be very helpful and productive!

If you want to see all the aliases you have defined, you can run alias by itself:

~
bsh ❯ alias
alias g.b='git branch -v --sort=committerdate'
alias g.c='git commit -v'
alias g.co='git checkout'
alias g.cv='git commit --no-verify -v'
alias g.d='git diff'
alias g.log='git log --graph --format=format:'\''%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(bold white)— %an%C(reset)%C(bold yellow)%d%C(reset)'\'' --abbrev-commit --date=relative'
alias g.p='git pull -r'
alias g.pf='git push --force-with-lease'
alias g.s='git status'
alias g.tree='git log --graph --pretty=oneline --abbrev-commit'
alias ls='eza -G'
alias src='source__'
alias vim='nvim'

Maybe I should clean these up sometime...

Search for Files with find

The find command allows you to search for specific file names in specific directories. The general usage looks like:

find <directory> -name <file_name>

find . -name cat_names.csv
find /bin -name ls

find is extremely flexible and has a variety of modes of operation. You can search for directories instead:

# find all directories in the current directory
find . -type d

You can use what's called a glob to search for files by their file extension:

find . -name '*.csv'

And you can filter by size, owner, modification date, and more.

Search Through Files with grep

Sometimes you want to find lines matching a pattern in a specific file or files. For this, you can use the grep command, which has a funny name originating from a common command in an older tool.

grep <pattern> <files>
[stephen@virtualbox ~] grep export .bashrc
    *) export PATH="${PATH}:${new_path_entry}" ;;
  export LLVM_INSTALL_PATH="/home/stephen/local/llvm16-release"
  export FLYCTL_INSTALL="/home/stephen/.fly"
export FZF_DEFAULT_COMMAND='rg --files'

This example searches my system configuration file called .bashrc for any lines that contain the text export. Although there's some mystery in the format, you can see that I use export in 4 different places in this file. Let's try another example:

[stephen@virtualbox ~] grep PATH .bashrc
    case ":$PATH:" in
    *) export PATH="${PATH}:${new_path_entry}" ;;
  export LLVM_INSTALL_PATH="/home/stephen/local/llvm16-release"
  path_add "$LLVM_INSTALL_PATH/bin"
  path_add "$FLYCTL_INSTALL/bin"


Notice that although I searched for "PATH", it also printed out lines that have "PATH" as part of another "word", like "LLVM_INSTALL_PATH". You can make it only look for "whole" words with the -w option:

[stephen@virtualbox ~] grep PATH .bashrc -w
    case ":$PATH:" in
    *) export PATH="${PATH}:${new_path_entry}" ;;

You can also print out the line numbers of each matching line with -n:

[stephen@virtualbox ~] grep PATH .bashrc -n
43:    case ":$PATH:" in
45:    *) export PATH="${PATH}:${new_path_entry}" ;;
51:  export LLVM_INSTALL_PATH="/home/stephen/local/llvm16-release"
63:  path_add "$LLVM_INSTALL_PATH/bin"
64:  path_add "$FLYCTL_INSTALL/bin"

And you can make the pattern match on any combination of uppercase and lowercase with -i:

[stephen@virtualbox ~] grep PATH .bashrc -i
path_add() {
  new_path_entry="$1"
  if [ -n "$new_path_entry" ]; then
    case ":$PATH:" in
    *:"${new_path_entry}":*) ;;
    *) export PATH="${PATH}:${new_path_entry}" ;;
modify_paths() {
  export LLVM_INSTALL_PATH="/home/stephen/local/llvm16-release"
  zig_paths
  path_add "/usr/local/sbin"
  # more of the above...
modify_paths

There are quite a few other options you can find in the man page!

The Pipe Operator |

Sometimes, you'll want to use the results of one command to feed into another command. Perhaps you want to get all the text files in your directory and order them by the file name. How would you do this? Well, we can use the pipe operator! So named because, well, it looks like a vertical pipe, and it sends data between commands. We could use it like this:

find . -name "*.txt" | sort

The sort command, as you may expect, sorts the strings it receives alphabetically and numerically, like a dictionary does.

However, if we have text files in a subdirectory, this doesn't quite work:

bsh ❯ find . -type f
./sample/file5.txt
./file1.txt
./file6.txt
./file2.txt
./file4.txt
./file3.csv

bsh ❯ find . -name "*.txt" | sort
./file1.txt
./file2.txt
./file4.txt
./file6.txt
./sample/file5.txt

We would expect "file5" to be before "file6", but because of the subdirectory, "file6" shows up first. To fix this, we can use a command basename:

bsh ❯ basename sample/file5.txt
file5.txt

What happens if we put this before sort in the pipe?

bsh ❯ find . -name "*.txt" | basename | sort
basename: missing operand
Try 'basename --help' for more information.

Well, that's not what we want. We can't use it directly, but we can use another tool called xargs to build a command from the results of the previous command:

bsh ❯ find . -name "*.txt" | xargs basename -a
file5.txt
file1.txt
file6.txt
file2.txt
file4.txt

The -a option tells basename to take multiple arguments and operate on them individually. And now, it works! They're out of order, but we can just slap a sort at the end:

bsh ❯ find . -name "*.txt" | xargs basename -a | sort
file1.txt
file2.txt
file4.txt
file5.txt
file6.txt

And now we know all the text files in and below our current directory, and they're sorted!

Simple Text-editing with nano

The most common thing you'll do in a typical software development career is typing text. For that, you'll need a text editor, and I think nano is the simplest one to start with. To start it, run:

nano <file_name>

As an example, I ran this to open one of my terminal configuration files: nano .bashrc

The nano text editor showing one of my configuration files

What I like about nano is the always-present control scheme guide at the bottom of the screen. The commands starting with ^ indicate the Ctrl key, starting with M indicates Alt, and although it's not a power tool, it colors text in the file based on the file type, and enables basic editing.

Outside of the special commands at the bottom of the screen, nano works exactly like Notepad or Google Docs, just without formatting. Typing adds characters to the file, while Backspace and Delete remove characters from the file. Shift accompanied by movement with the arrow keys or the back/forward/next word/previous word commands selects an area of text that you can cut, copy, and paste.

It's pretty simple, but effective.

Complex Text-editing with emacs or vim

This section could easily be multiple encyclopedias' worth of introduction, so this is just a brief note on the world of text editors. In the terminal world of editors, emacs and vi are the two historical options. vi itself isn't used much today, but it inspired vim, which later spawned my personal choice of editor, Neovim.

You might not know that the two editors have a very contentious history in what's called the editor war. Software developers have held very strong opinions for a long time.

Whether you choose to use a terminal editor or a GUI editor, just remember that whatever you enjoy using is the best tool for you! Some developers vibe better with vim or emacs, some will prefer something like Visual Studio Code, and some will enjoy a more full-feature solution like JetBrains or Visual Studio (distinct from Visual Studio Code).

Lists for Further Reading

If you want to see a different list of a lot of terminal commands, there's a great list from DigitalOcean on the top 50+ commands! That's a lot!