My Command Line Usage

Using the command line - known on MacOS as terminal - can be daunting given its use of written commands instead of a graphic interface. However, it is a very powerful tool and can often be a more productive and effective way to execute programs.

For example, what if you have 20 compressed files and want to search for "error" in all of them, then save those instances into a file? Without the command line, you would have to

  1. uncompress the files
  2. manually search each file for "error"
  3. copy-paste each instance to a file (who knows how many there will be!)
or using an app
  1. find an app that does exactly (or close to) what you want
  2. download it, hopefully it is free, hopefully it is trusted
  3. familiarize yourself with the UI
  4. get the app the output each instance of "error" in all your files to a new file

But with command line, you can do this in one line!

zgrep -i "error" *.gz > output.txt

And that's just the tip of the iceberg! Now that you see the power, let's get into some of the basics of the command line.

A program is the actual code that will run to execute the action you request. A program will have parameters, also known as arguments or flags (see the history and details about their naming here. For all practical purposes, they are used interchangably), that can be added so you can invoke more specific actions. Together, the program and its arguments make up a command that you can execute in the command line.

The interface you see, like the terminal, to execute commands to the command line is also called the Command Line Interface (CLI). You'll often see CLI tools mentioned, so that's what people are referring to.

Arguments often have a short form that can be invoked with -. The long form name can be invoked with --. You can invoke the short form with [command] -[argument 1] -[argument 2] and with [command] -[argument 1][argument 2] such as ls -la.

First, lets go through a few basic knowledge about your computer. Your operating system has folders, also known as directories. On MacOS and Linux, the root directory is /. Every other directory can be reached from /. When you open a new window in your command line, you are plopped into the default directory. Think of the directory system as a tree (hence the term "root"). So you can have /Users/daniloradovic/Desktop.

The home/default directory is denoted as ~. Mine is at /Users/daniloradovic.

You will often see $ or some other symbol to denote the input to the command line - I will use $.

What is actually running the program that you execute in the command line? This is called the shell. You can think of it as a program that runs your programs. You are entering commands to the shell as input and it can output results to the standard output (stdout) which is what you see outputted to the screen.

Storing commonly used commands

Some commands you may use very often and its a pain to keep writing them out. For example if you use git version control for and use git pull origin mainline --rebase. Well, to make life easier you can give the command an alias and invoke the alias instead of the full command - for the aforementioned command you could use an alias like grm.

First, identify which shell your machine is using. Run echo $0 - it will likely return "bash" or "zsh". You can also run echo $SHELL. If bash, you add the aliases and exports to ~/.bashrc. If zsh, you add them to ~/.zshrc. See my personal ~/.zshrc setup here!

Run source ~/.bashrc or source ~/.zshrc and your aliases should now be useable.

Okay - let's jump into the commands! I am listing out my most commonly used beginners commands and arguments, so for some commands I list out more information than others when required. I will have another article for with some more commands that I use less commonly or are more intermediate.

man

You can run man [name of program] to see the manual for the program. This gives you the description and a list of all the arguments that can be used with the program. This is super useful if you are having trouble finding an explanation for what a command and its arguments do, or want to see a list of arguments you haven't encountered before!

echo

echo will print out to the standard output (stdout) the arguments you provided it.

$echo "hello"
hello
$echo blah.txt
blah.txt
Notice that even though blah.txt is a file with contents, the actual string "blah.txt" is returned.

pwd

pwd shows you which directory you are in at any given time

$ pwd
/Users/daniloradovic/Documents

cat

cat outputs the contents of a file to the standard output.

$ cat foo.txt
Hello there

cd

cd allows you to change directories. There are a bunch of very useful arguments, all of which tell the program which directory you want to go to. When using actual paths instead of shortcut arguments, you can either input a relative or absolute path. Relative means relative to the current directory you are in, absolute means relative to the root. An example of moving to an absolute path is:

$ cd /Users/daniloradovic/Desktop

You can move down into a folder or move up into a higher level folder with relative paths Relatively moving into a folder within your current directory:

$ cd Desktop

Relatively moving into a nested folder within your current directory:

$ cd Desktop/testFolder1/testFolder2

Here are some common shortcuts for relative movement. Relatively moving out of a folder one level up the tree:

$ pwd
Desktop/testFolder1/testFolder2
$ cd ..
$ pwd
Desktop/testFolder1

Relatively moving to the same folder you're in:

$ cd .

Note that cd ./testFolder1/testFolder2 and cd testFolder1/testFolder2 do the same thing. Moving to the last directory you were at before the current one:

$ pwd
Desktop/testFolder1/testFolder2
$ cd ..
$ pwd
Desktop/testFolder1
$ cd -
$ pwd
Desktop/testFolder1/testFolder2

ls

ls will print out the files and folders in your current directory

$ ls
Desktop
Pictures
Documents

arguments:

ls -l will print out the files/folders in the current directory in addition to a lot of other information such as permissions, user/owner, size in bytes, and date created/

ls -a will print out all files - even hidden ones. Hidden files often have a . in front of it such as .zshrc

Mini lesson: Permissions

When doing ls -l you will see something like -rw-r--r--@. This denotes read, write, execute permissions for each user group. A dash represents a lack of that permission for the group. Here is what it will look like giving everyone permissions to do all 3 actions for a file:

-rwxrwxrwx@

And for a directory:

drwxrwxrwx@

You can see rwx is repeated 3 times. The first group represents permissions for Users (the current user), the second for Groups, the third for Others. The d means the object is a directory, a dash represents everything else, such as a file or program.

chmod

Now that you know about permissions, you can use chmod to change the permissions of a file or directory. The general structure is chmod [groups]+[permissions] "[file]" where groups can be any permutation of u, g, and o and permissions can be any permutation of r, w, and x. For example to allow users, groups, and others to have full read, write, and execute permissions of a file, you would run

$ chmod ugo+rwx "foo.txt"

As a shorthand, you can use chmod a+rwx to represent changes across all the permission groups.

sudo

You can put sudo in front of the rest of a normal command in order to execute the command as the root super user of the machine. This can sometimes let you take actions you normally wouldn't do, such as changing core values of the machine itself. This can be dangerous and corrupt your entire machine! So, only use sudo when you know it is safe.

mkdir

mkdir makes a directory in the path that you specify

$ ls
foo.txt
$ mkdir ~/Documents/bar
$ ls
bar
foo.txt

grep/zgrep

grep allows you to search for strings within uncompressed file (of type .txt, .log, etc). zgrep is the same as grep but works for compressed files like the .gz file type

$grep "error" ~/Documents/*
some useful arguments are:

Another useful argument is -E (or you can simply use egrep) which allows for extended regular expression matching. This allows for easy use of logical operators and pattern matching:
grep -E "foo|bar" test.txt will return lines that have either "foo" or "bar" and grep -E "foo.*bar" test.txt will return lines that have both "foo" and "bar".

Note that printing to the standard output can be very inefficient and clunky; it also prevents easy further searches into the output and other manipulations of the data. So, often you can pair grep/zgrep with > to print the output to a file as such

$ grep "error" ~/Documents/* > errors.txt

cp

cp stands for copy; you can provide it a source file or directory and destination file name or directory name

$ cp foo.txt bar.txt

Use -r when copying a folder. Use -i to display a confirmation message before doing the copying

rm

rm removes a file. To remove a directory, use rm -r.

$ ls
$ rm bar.txt
$ ls
$ rm -r bar
Note: you can use -f to force a deletion, but be very careful with this because it is irreversible!

find

find will find specified files or directories in the path you specify. The basic usage is

find [path to search in] -name "[query string]"
searching for file names

$ find ~/Documents -iname "blah*"
./blah.txt

searching for file types

$ find ~/Documents -iname "*.txt"
./blah.txt
./foo.txt
./bar/test.txt

Note that it will return all results that match the criteria no matter how many levels deep in the directory from the starting path. To specify the depth, you can use -depth [number].

$ find ~/Documents -depth 4 -iname "*.txt"

This returns only the results at that depth, not everything from depth 1 to [number]. You can use -maxdepth [number] to return everything from depth 1 to [number].

$ find ~/Documents -maxdepth 4 -iname "*.txt"

The above is just some simple examples; find is extremely powerful and has a ton of arguments that can make your searches very advanced. Go check them out with man find.

wc

wc stands for wordcount. Running the program with just the file as input yields

$ wc foo.txt
  1       2      12 foo.txt
representing lines, words, and characters in the file. The below are my most commonly used arguments:

&

This allows you to run a command in the background so you can continue using your shell to run other commands. You append it to the end of the command you want to run in the background, such as grepping a folder containing hundreds of files

$ grep -i "error" * &

>

This is called a redirect. This allows the output of command 1 to be the output for a file. If the file already exists, its contents are overwritten.

$ echo "Hello there" > foo.txt
$ cat foo.txt
hello there

>>

Is the same as > except it appends to the existing content of the file provided - if any. It does not overwrite the content

$ echo "Hello there" > foo.txt
$ echo "General Kenobi" >> foo.txt
$ cat foo.txt
Hello there
General Kenobi

;

[command 1 ]; [command 2] allows you to execute command 1 first then execute command 2 after command 1 finishes. There is no interaction or information passed between the two.

$ cd ~/Documents; ls 
foo.txt
bar

&&

[command 1 ] && [command 2] will execute command 1 and then only run command 2 if the first did not fail. A basic example is making an empty file and filling it with contents only if the file creation succeeded.

$ touch test.txt && echo "Hello there" > test.txt
$ cat test.txt
Hello there

df

Displays the free disk space. I use df -h for "human-readable" output.

$ df -h
  Filesystem       Size   Used  Avail Capacity iused      ifree %iused  Mounted on
  /dev/disk1s5s1  234Gi   23Gi   57Gi    29%  563932 2448561428    0%   /
  devfs           191Ki  191Ki    0Bi   100%     662          0  100%   /dev

Identify which process is using a port

You can use any of the 3 below commands to figure out which process is using a port of interest (in this example, port 8000):

$ fuser -n tcp 8000
$ lsof -i :8000
$ curl http://localhost:8000 -v

kill

This will kill the process running on a port. Use the -9 argument to do a "non-catchable, non-ignorable kill"

$ kill -9 [port]

ssh

ssh lets you access the command line of a destination host directly from a source machine, like your laptop! To connect, you may simply run

$ ssh [host@host.com]

For more advanced usage of ssh, take a look at this as a great resource for port forwarding/tunneling.

open

opens the file or directory in the default program. For me, directories open in Finder and files open in TextEdit

$ open blah.txt
$ open .  // remember, . means the current directory

vim

Vim is a program that allows you to view contents of a file and edit them; it is very powerful and has its own commands within the program itself. My preference is to use vim through ther command line when I need to quickly view, search, or edit files within minutes or needing to navigate across the file. In other cases like needing to work on Java code in long files or codebases where there are methods sprawled across many files, I'll use a code editor like VSCode.

Vim is still very important for software developers to be familiar with because a lot of times machines that you need to ssh into that hold your logs will have no option for easily viewing files besides Vim or Emacs (a similar program). It may also be difficult or a hassle to always open them in a code editor given they live on a machine other than your development machine. Below are just the bare basics for Vim that allow you to enter/exit the program. See my Command Line Usage Part Two article for a more practical guide on Vim.

To enter vim, just type vim [file name]. To exit vim, type :q or if that doesn't work try :q! which will force quit.

vim works in modes, such as a write mode. To exit any mode, simply press the escape button.

There's a ton of other useful commands, which I will detail in another article. However, to just get you started with vim, enter into a file and press i. Now you are in write mode and can edit the file.

Note: some machines don't have vim pre-installed. Most of those should have vi installed, which is an earlier version of vim (vim actually stands for Vi IMproved).