Basic Unix/Linux Shell Commands
Including syntax examples and file redirection
This list of commands and examples was originally compiled for my friend Jonah who is just starting a journey into computer science. I thought I would clean it up and share it with the world. If you are brand new, remember that the dollar sign '$' represents the prompt, not the command itself.
Unsure if this is for you? If you use any of the following OSes, the commands in this guide will work:
- Apple macOS, OS X, OS 9
- Terminal.app, iTerm
- GNU/Linux (Debian, Ubuntu, Mint, Arch, etc.) and derivatives
- Xterm, termite, st, urxvt
- FreeBSD, OpenBSD, NetBSD
- Android mobile OS
- A web server that is not Windows
The examples in this tutorial will NOT work on vanilla Windows without Cygwin, or a Linux environment in a virtual machine like VirtualBox. These shell commands and examples will work with the majority of popular options for shells, including GNU Bash, Z Shell, and Fish. If you don't know what your shell is, it is probably Bash.
If you are confused about any of these commands (for instance, cat), you can simply look up the manual pages for them in the command prompt in your Terminal with $ man cat, $ man echo, man grep, etc.
$ cat > filename.txt
Create a new file with name filename.txt, and put whatever you type on standard input into it. Note: If filename.txt already exists, this method deletes whatever is in it.
$ cat >> filename.txt
Append standard input (anything you type, sent with enter/Return) to the end of existing file filename.txt.
$ cat filename.txt | grep "example"
Search filename.txt for the string "example". The shell will print the line or lines in which the string exists to standard output.
$ echo "hello world" > hello.txt
Create a new file hello.txt, and write the string "hello world" into it.
$ echo "normal command" > /dev/null
Any commands redirected to the device file /dev/null will discard what would normally appear on standard output. So the echo command doesn't print anything.
Notice how whenever you type an invalid command, your shell will alert you with a message saying command not found. This behavior of the shell can be circumvented by redirecting the standard error, a stream independent of standard output (stdout). Instead of using a "greater than" symbol to redirect output, use a 2 and a greater than to redirect stderr.
$ badcommand 2> /dev/null
This command is invalid, but you wouldn't know it, because it doesn't print an error.
$ cat longfile.txt | head -n 5
Print only the first 5 lines of "longfile.txt". Because of the pipe symbol |, the output of the cat command is used as the input for the head command. The head command then prints to the shell's output, where you, the user, can see it.
The -n bit is called a "command line argument", and you will come to learn, by reading man or tldr pages, that the command line world is full of them. Compiling a C program involves sometimes tens or dozens of separate command line arguments. Fortunately, you normally don't have to memorize this many. An argument like "-n" needs a value, and this value is given after a space. If you do not provide a number after the entry head -n, the head program will say head: option requires an argument -- 'n'.
$ cat longfile.txt | tail -n 5
Print the last 5 lines of "longfile.txt". Like you can probably gather, a command like tail -n 20 would print the last 20 lines of the long file.
$ tar czf big_archive.tar.gz big_directory &  11047 $
Note the ampersand. Type an ampersand after a command, to run it in the background, or "fork" it. A typical tar command with a lot of files may take a long time, and you wouldn't be able to continue to run commands without an ampersand &. What this little symbol does, is tell your shell (Bash, ZSH, Fish) "Hey, I want to put this one task off to the side, and then keep doing other stuff while it runs. I don't want to be stuck waiting for it to finish." Your shell responds, "Okay, here's the PID for the task you just set aside."
What this means though, is that you won't be able to exit your shell session, without first returning the background process to the foreground with the command $ fg, and either waiting for it to finish, or terminating it. Your terminal app, on any operating system, will prevent you from closing it if you have running jobs that were previously set aside with the initial ampersand. It is possible to kill the Terminal containing the stalled job (process), but doing so will terminate any currently running processes inside of it. By the same principle, if you're working on a Word or Pages document, or an Audacity song, and immediately pull the plug to your own computer, turning it off, your recent changes will obviously not be saved, and then your hard drive will have to be checked with fsck next time the computer boots.
When the set-aside background process finishes, your shell (Bash and ZSH I've tested anyway) will tell you with a message like this.
$ echo hello world # Bash...  + Done tar czf big_archive.tar.gz big_directory $ echo hello world # Zsh...  + 11047 done tar czf big_archive.tar.gz big_directory
This is called forking and it is a powerful and strategic time-management technique in both Windows and Unix-based worlds.
REMEMBER: When typing a command, the space character " " is used to separate command-line arguments. Because of this, filenames that have spaces in them must be wrapped in double-quotes or have each space escaped individually with a backslash ('\').
This is BAD:
$ cat Filename With Spaces.txt
That is completely wrong. How the shell interprets this command is, it thinks you're trying to open three separate files instead of just one, "Filename", "With", and "Spaces.txt", none of which actually exist. It will tell you cat: Filename no such file or directory, With no such file or directory, etc.. The examples below use a single argument to cat, instead of three like the one above.
This is GOOD:
$ cat "Filename With Spaces.txt" $ cat Filename\ With\ Spaces.txt
|Command||What It Does|
|$ cd [directory]||Change current directory|
|$ pwd||Stands for "print working directory". This path to the current working directory is also stored in the variable $PWD|
|$ ls [directory]||List files in the given directory, or current directory if none provided.|
|$ ls -l [directory]||List files in the given directory, in long form with owners, permissions, and last modified dates.|
|$ echo "string"||Print string on standard output.|
|$ nano [file]||Edit file in Nano text editor, an easy editor for beginners. Or, create a new file if no filename is given. The controls are visible on the screen while editing. Press Control + X to quit, then follow the prompt. Press Control + W to write the current buffer. Control + K cuts the current line, Control + U pastes.|
|$ vim [file]||Edit file in Vim, an intermediate text editor, or create a new file if no filename is given. Unlike nano where you can type immediately and move with arrow keys, Vim has the Insert Mode, which can be entered by pressing the I key, and the Command Mode, which can be returned to by pressing ESC (Escape). Type ":q" in command mode to quit. ":wq" will write to the file, and then quit. ":w" will write the current buffer to the file, if one has been given. If not, you can give it an argument like this: :w filename.txt. This will create a new file "filename.txt".
Vim uses the keys HJKL for moving the cursor around the file in Command Mode. But in Insert Mode, the letters H, J, K, L will manifest when you press their keys.
|$ touch [file]||Create a new empty file.|
|$ tar xvf [name of archive file]||Decompress a .tar or .tar.gz archive file.|
|$ tar cvzf [output filename] [file or dir to compress]||Compress a given file or directory into a .tar.gz archive. This "v" here stands for "verbose", and what that means is "print all of the files that pass through". The tar command wouldn't print anything without "v", it would be "silent". The "v" option will almost always mean verbose mode in many programs.|
|$ gzip -d [archive.gz]||Decompress a Gzip format archive.|
|$ gzip -c [filename] > output.gz||Compress a single file. These Gzip commands will only work for single files; to compress directories, you must use tar, which has Gzip built-in. This is why Unix compressed archives will have names that end in ".tar.gz"|
|$ mv [source] [destination]||Move or rename source file, to a new filename, or existing directory.|
Reminder: The directory names . (a single period) and .. (two periods) are very special, and they refer to different places, relative to the current directory: the current directory itself, and the parent directory, respectively.
A directory that's just a dot refers to the current directory itself. So the command ./script.sh will execute the script "script.sh" in the current directory. The double dots refer to the parent directory. So a command like $ mv file.txt .. would move file.txt into its parent directory, one layer above its current one in the tree.
|$ cp [existing file] [new filename]||Copy an existing file to a new name.|
|$ cp -r [existing dir] [new dir]||Copy an entire directory with its contents to a new directory.|
|$ rm [unwanted file]||Delete a file permanently.|
|$ rm -rf [unwanted dir]||Delete an entire directory, permanently.|
|$ mkdir [name]||Create an empty directory.|
|$ mkdir -p path/to/new/dirs||Create a new directory with a path/parent directory that might not exist yet, and create the path if it does not. In this example, if the folder "new" does not exist, the command creates it, and the new empty directory "dirs" inside of it.|
|$ rmdir [existing dir]||Remove an empty directory. To delete an entire non-empty directory, use rm -rf.|
|$ sudo [command] [arguments]||Run command as root / superuser.|
Note: If you try to do something and get an Operation not permitted error, that means your user doesn't have sufficient permissions for the task you're trying to do. Run the exact same command with sudo before it, and you will be prompted for an administrator password.
Remember, you will not actually *SEE* your password as you type it. This is by design. As a shortcut, you can just run sudo !! to run the previously entered command, with sudo before it, giving it the required permissions.
Example: $ nano /etc/hosts will open the file, but will warn /etc/hosts is unwritable, and it will not let you write to it.
However, $ sudo nano /etc/hosts will prompt for the password, and then open nano with the file, allowing you to save your changes. As a Unix tutor of sorts, I must warn you DO NOT Fuck Around with Sudo if you don't know exactly what you're doing. You could end up accidentally doing something you'll regret, and can't undo. If you need to run multiple commands as root, you can run the command $ su root to spawn a shell with admin privileges. This will create a root instance of Bash or Z Shell.
Your operating system may prohibit your use of sudo at first, but you can give yourself privileges by editing your sudoers file. To actually have permissions to write to the /etc/sudoers file, you must first log in as root with $ su root, and then run the command # visudo. Once this file is correctly changed, you can freely run sudo as your own user.
|$ man [command]||Open the manual page for the specified command or program. The man database also has C programming library information, and configuration tutorials for various programs. This is an interactive reader. Press "q" to quit.|
|$ cat file.txt | less||Read the contents of the file file.txt in an interactive reader: less. In this reader, you use the up and down arrows, and the space bar, to navigate the file, and the key "q" to quit. You can pipe the output of any command you wish into less, and this is good for commands like $ ls -la or $ ps -au, that have a lot of output.|
|$ ls -la ~ | more||This command will create a long listing of the files in your Home directory (noted by the tilde), including invisible files, and send it to an interactive reader more. In this reader, you see the content one screen's worth at a time, and you advance to the next with the space bar.|
|$ which [command]||Show the path to the executable binary (or the shell function, or alias) of the given command
For example, $ which echo will either return /bin/echo or /usr/bin/echo.
|$ df -h||Show disk usage and remaining free space for all mounted drives. The -h option means "human readable": without this option, df shows the exact number of bytes.|
|$ grep "expression/pattern" [filename]||Search the given file for a string or pattern. You can also pipe output from another command into grep, to search that output, as I showed you in the example above. You can use options like --context=5 to show the surrounding 5 lines around the matching line, or --exclude to exclude certain files or directories.|
|$ grep -rnw "expression" [directory]||Recursively search all files in a directory, and its sub-directories, for the given string or pattern. This command shows line numbers, filenames, and searches strictly for the pattern by itself (ignores matches that are part of another word or phrase).|
|$ chmod +x [filename]||Give executable permission to the specified file. You will have to do this if you write a shell script. The command $ chmod 755 [filename] does the same thing. You can also use +r or +w for read and/or write permissions, respectively.|
|$ chown [username]:[group] [filename]||Change ownership of the specified file or directory to given username and group (if this command is used on a directory, it will only affect the dir itself, not its contents).|
|$ chown -R [username]:[group] [directory]||Recursively change ownership of the specified directory, and its contents, to given username and group. Please be careful with chmod and chown, especially when used with sudo. If a certain file or directory has the wrong permissions, it could affect the functioning of the whole system.|
|$ chgrp [new group] [filename]||Change the owning group of the given file. The default group for your typical user is most likely the same as your username. You may also use the user IDs and group IDs, instead of usernames or group names.|
|$ kill [PID number]||Kill a process, given a PID (process ID) number. You can use top or $ ps -aux to retrieve process IDs, their users, and their process names.|
|$ pkill -x "process name"||Kill any process with the provided process name, instead of a single PID. For example, $ pkill -x xterm will kill all instances for Xterm currently running. Remember, you will not be able to terminate root-user processes without using sudo.|
|$ killall "process name"||Kill any process with the given name, same as above. This command is not available in every environment.|
|$ ps -au||List currently running processes with PIDs, users, full process names, CPU usage, and memory usage. You can pipe this into the command less if you want, for easier viewing.|
|$ top||Interactive program that lists running processes with PIDs, users, process names, CPU usage, and memory usage. Press "q" to quit.|
|$ pgrep "process name"||Retrieve the first PID for the given process name.|
|$ history||Shows the entire command history for the current user. You can pipe this command into tail to show the last 10, 20, or however many previous commands you want, like this: $ history | tail -20.|
|$ ping google.com||Send small packets to, and receive packets from the server google.com.|
|$ ssh [username]@[IP or domain name] -p [port]||Connects to a remote host and opens a secure shell. This can be another computer, a Raspberry Pi, an Android phone, a website, or a Minecraft server.|
For example, this is how I log into my own website (this website).
This command will prompt you for the host user's password, before connecting to the specified host. You can use ssh-keygen, ssh-add, and ssh-agent, to create a key for yourself to synchronize with the target host, so you wouldn't have to type a password every time. I recommend you look this stuff up yourself. It is possible to run the command $ ssh 127.0.0.1 to simply open a new shell on your own machine.
Not only can you connect to websites or servers you manage, you can connect to other Unix/Linux devices on your local network. For example, if I wanted to log in to my girlfriend's MacBook from my Linux laptop, I would do this.
IP addresses that begin with 192.168. are special, they all represent devices connected to your local network. These 192 addresses will only work for you, and others, when you're connected to your own WiFi. Here's an easy way to learn this. You can almost always change the configuration for your router/modem (including SSID, password, and port forwarding) by visiting http://192.168.0.1 in a web browser. That link should take you to a login screen for your own wireless modem.
|$ scp [file] [username]@[domain or IP]:[directory]||Send the specified file to the target computer or server over SSH. This will prompt for the target user's password, and it will place the given file in the target directory. You can use this command to copy several files to another computer on your local network, without having to physically connect to it. You can use the -r option (meaning "recursive"), before the folder name, to copy an entire directory to a target machine.|
Here's an example. If you wanted to copy the whole album The Low End Theory by A Tribe Called Quest to another computer or device on your network, you would type something like this, and you would see the following output.
When this operation is finished, the user "kristin" should have a new folder in her Music directory called The Low End Theory 320 MP3, with the jazz hop classic from 1991 inside of it.
|$ eval $(ssh-agent)||Start the SSH agent in the background. This program is in charge of holding and exchanging private and public SSH keys. This command must only be executed this way.|
|$ ssh-keygen -t rsa -f [output file]||Generate a RSA key pair, public and private. This will prompt you to come up with a passphrase, and will produce a fingerprint. Say you wanted to generate a key pair so you don't have to type your GitHub password every time you push. You give a name like github_rsa for the filename, ssh-keygen will create two files: github_rsa and github_rsa.pub, the private and public key respectively. You will want to keep these files in the hidden directory ~/.ssh, which should be automatically protected with proper permissions. At this point, you can copy the contents of github_rsa.pub into the text field on your GitHub account's SSH Keys page, and then GitHub will let you push commits without typing your password, as long as you have ssh-agent running and you've added your new key to it.|
|$ mount [device file] [directory]||Mount the device file to the specified directory. Every external drive, whether its an iPod, an iPhone, a flash drive, or an external hard drive, has a device file in the /dev directory. Your internal hard drive, which is mounted at /, is almost always at /dev/sda1 or /dev/sda2.|
Example: $ mount /dev/sdb2 /mnt/iPod
You can then read files from the connected iPod by cd-ing into /mnt/iPod.
|$ umount [mount point]||Unmount a previously-mounted device, using the same directory previously used in the first mount command. You MUST unmount any physical devices before you physically unplug them from your computer. If you don't take the time to umount before unplugging, your external file system may become corrupted, and may require a fsck. This program will not let you unmount your device if there is still a process or shell interacting with it. This might seem inconvenient, but it is actually a security feature to prevent bad things from happening to the external device. Windows behaves exactly the same way, except on Windows it's called "Safely Remove Hardware" or "Eject Safely". Mac OS will provide a convenient "eject" button next to your device names.|
Example: $ umount /mnt/iPod
|$ whoami||Print the current user's name. This is also in the environment variable $USER.|
|$ cal [year]||Print a calendar for the specified year, or the current month if no arguments.|
|$ date||Print the current date and time.|
|$ dd if=[input file] of=[output file]||Use this command to format a device, or flash an ISO or IMG image to a device file. This will delete all data in the output file.|
|$ export [variable name]="variable content"||Create an environment variable with specified content.|
|$ id [username]||Show all of the groups that the given user belongs to.|
|$ ifconfig||List all network interfaces.|
|$ find [directory]||List all files recursively in the given directory. This may take a while for large directories.|
|$ useradd||Create a new user, or add existing user to groups. Only works with root or sudo.|
|$ usermod||Modify user details. Only works with root or sudo.|
|$ systemctl [action] [service]||"Action" should be either "start", "stop", or "restart". This is a SystemD utility for starting and stopping background services or daemons.|
|$ pushd [directory]||Change to the specified directory, like you would with cd. This command adds the new current directory to the "stack" of directories in the shell. This means that when you popd, you will return to your initial directory.|
|$ popd||Pop the directory from the "stack", and return to the initial directory. Both "pushd" and "popd" will echo all directories in the stack.|
|$ curl [URL] > outputfile||Fetch the HTML content (or image, or any other file type) that the given URL responds with, and save it in outputfile.|
|$ wget [URL]||Retrieve the content at the given URL, same as curl.|
|$ cmp [one file] [other file]||Compare the two files. What this means is, return 0 "success" if the two files are exactly the same, or return 1 "error" if the two files are different. This command will tell you where the first difference is. The return code of any command can be looked at with the variable $?.|
|$ sleep [number of seconds]||Pause the shell for the given number of seconds.|
|$ ln -sF [existing file] [name of link]||Create a symbolic link. Symbolic links are essentially "shortcuts", tiny files that point to other files or directories. You can create a symbolic link to a directory, and it will appear to be a separate place, but really it's only one place. The symbolic link will apply all changes you apply to it to the original file!|
|$ wc [filename]||Word counter. This command tells you how many lines, words, and bytes are in any given file, respectively.|
|$ pgrep [process name]||Print the Process ID of the process with the given name.|
Okay, so this is a lot to take in.