Logo

Linux Commands, Part 1

23 min read
Lesson slides
1 / 17

Operating Systems I - Lesson 9

Linux Commands, Part 1

Navigate the filesystem, manage files, view and edit text, control permissions, search, and archive a project entirely from the terminal.

By the end of this lesson you will be able to navigate the Linux filesystem, manage files and directories, view and edit text, control permissions, search text, and archive a project entirely from the terminal.

Objectives

  • Read and understand the Linux shell prompt and general command syntax
  • Get help for any command using man and --help
  • Navigate the filesystem confidently with pwd and cd, including the ~, .., and - shortcuts
  • List directory contents with ls and read the output of -a, -l, -lh, -F, and -d
  • Work faster using tab completion and command history
  • Create, copy, move, and remove files and directories safely
  • View and edit text files with less, cat, head, tail, and nano
  • Redirect command output into files with echo using > and >>
  • Explain Linux file ownership (user, group, others) and read it from ls -l
  • Change permissions with chmod using both the symbolic and the octal method
  • Search text with grep, including piping the output of ls into grep
  • Archive and compress files and directories with tar, gzip, and zip
  • Compare a directory's size against its own archive with du -sh

Prerequisites

  • Access to an Ubuntu Linux terminal (a local installation, a virtual machine, or a remote server reached over SSH)
  • A regular, non-root user account (mistakes stay contained when you are not working as the administrative root account)
  • Ability to open a terminal window
  • No prior command-line experience is assumed

This is Part 1 of a two-part lesson. System information commands such as df, free, hostname, date, cal, whoami, id, and ping are covered in Part 2.

The Shell, the Prompt, and Command Syntax

The shell reads the text you type, runs it as a command, and prints back the result. The line where you type is the prompt; on Ubuntu it usually shows your username, hostname, and current directory, shortened to ~ for your home directory.

Every Linux command follows the same general pattern:

command [options] [arguments]
  • command: the program to run
  • options: flags that change the command's behavior, usually written with a leading - (short form, such as -a) or -- (long form, such as --all)
  • arguments: the files, directories, or other data the command should act on

Multiple commands can be placed on one line by separating them with a semicolon ;, and each runs one after the other. For example, cd /usr/share; pwd first changes directory, then prints the new location.

Two help tools are always available. man opens a command's full manual page; man -k keyword searches by keyword, and man -f command looks up the exact page for a name. --help, attached to almost any command, prints a short usage summary straight to the terminal instead:

man chmod
chmod --help

The first line opens the full manual page for chmod; the second prints a brief summary of chmod's options without leaving the prompt.

Navigating the Filesystem with pwd and cd

When you log in, you are placed in your home directory, a location set aside for your own files. pwd (print working directory) shows the full path of your current directory, measured from the top-level root directory, written as a single slash /:

pwd
[Output]
/home/student

cd (change directory) moves you elsewhere. An absolute path starts with / and describes a location from the root directory, unambiguous from anywhere. A relative path has no leading slash and describes a location starting from where you currently are.

flowchart TD
    root["/ (root)"] --> usr[usr]
    root --> home[home]
    usr --> share[share]
    share --> locale[locale]
    locale --> en[en]
    en --> LC[LC_MESSAGES]
    home --> student["student (home directory)"]

From the home directory, cd /usr/share is an absolute move to share in the diagram above. From inside share, cd locale is a relative move one level down. Continuing from inside locale after that move, cd en/LC_MESSAGES is a relative move two levels down at once, landing in LC_MESSAGES. (The exact folders present under /usr/share vary from one system to another; this is only one example path used to illustrate absolute and relative moves.) Three special references make movement quicker:

  • ~ always means your home directory, so cd ~ (or plain cd with no argument) returns you there from anywhere
  • .. means the parent of the current directory, so cd .. moves up exactly one level
  • - means the previous directory you were in, so cd - jumps back to wherever you were before your last cd

Listing Directory Contents, Tab Completion, and History

The ls command lists the files and subdirectories inside a directory. Run alone it hides files whose name starts with a dot, since these are treated as hidden. Useful options can be combined:

  • -a shows all entries, including hidden files, plus the special . (current directory) and .. (parent directory) entries
  • -l shows a long, detailed listing: file type and permissions, number of links (how many hard links point to this file; an ordinary new file shows 1), owner, group, size, last modified date, and name
  • -lh is the long listing with file sizes shown in a human-readable form (KB, MB, GB) instead of raw bytes
  • -F appends an indicator character after certain entries so you can tell entry types apart at a glance; on the directories used in this lesson, that indicator is a trailing / after directory names
  • -d shows information about a directory itself rather than listing what is inside it, most often combined with -l as ls -ld
ls -a
[Output]
.  ..  .bashrc  .profile  file1

Here .bashrc and .profile are hidden configuration files, and . and .. are the built-in references to the current and parent directory described above.

Typing full names is slow and error-prone, so the shell offers tab completion: type the first letters of a name and press Tab to complete the rest automatically. The shell also keeps a command history: the up and down arrow keys step through commands you already ran, and history lists them numbered, so a past command can be re-run with ! followed by its number, for example !42 re-runs whatever command history numbered 42 (!! re-runs the last command instead). That list can be filtered by sending it into grep with the pipe operator | (explained fully later in this lesson), as in history | grep chmod, which narrows the list down to lines containing a chosen word.

Creating, Copying, Moving, and Removing Files and Directories

The touch command creates a new, empty file, or, if the file already exists, only updates its last-modified timestamp:

touch file1

The mkdir command creates a new, empty directory. Normally its parent directory must already exist; the -p option removes that requirement by creating every missing directory along the given path in one step:

mkdir -p lab9_project/docs/reports

This single command creates lab9_project, then docs inside it, then reports inside docs, even though none of them existed beforehand.

The shell also expands a comma-separated list inside curly braces, {a,b,c}, into several separate arguments before the command ever runs, which lets mkdir create multiple sibling directories in one call:

mkdir -p project/{docs,scripts,data}

The shell rewrites this into mkdir -p project/docs project/scripts project/data before running it, so all three directories are created inside project in a single command.

cp copies a file to a new location or name, leaving the original in place. -r (recursive) is required to copy an entire directory with everything inside it. -i (interactive) makes cp ask for confirmation before overwriting an existing file, guarding against accidental data loss:

cp -i notes.txt notes_backup.txt
cp -r docs docs_backup

The first line copies notes.txt to notes_backup.txt, asking first if that name already existed; the second recursively copies the entire docs directory, including everything inside it such as reports, into a new docs_backup directory.

The target's type changes what "copying to an existing name" actually does. Copying to an existing file name overwrites that file's content, which is exactly what -i guards against above. Copying to an existing directory name behaves differently: the source is placed inside that directory instead of replacing it. For example, once docs_backup exists, cp notes.txt docs_backup does not overwrite docs_backup; it creates a new copy of notes.txt inside it.

mv moves a file or directory to a new location, and is also how items are renamed, since renaming is just "moving" something to a new name in the same place:

mv draft.txt docs_backup/
mv docs_backup archive_backup

The first line moves draft.txt into docs_backup; the second renames docs_backup to archive_backup. If the destination already exists as a file, it is silently overwritten and cannot be recovered, so care is needed with both cp and mv. The same -i flag used with cp also works with mv, prompting for confirmation before it would overwrite an existing destination: mv -i draft.txt docs_backup/.

rm removes files; rmdir removes a directory only if it is completely empty (it refuses otherwise). To remove a directory and everything inside it, rm needs -r (recursive), written as -R in some references, which works the same way; adding -f (force) also suppresses confirmation prompts, convenient but risky since it deletes without asking. There is no undo for rm:

rmdir empty_folder
rm -r archive_backup
rm -rf temp_folder

These three lines are independent illustrations, not a continuing sequence on the same folders: the first removes a directory named empty_folder and would fail with an error instead if it were not empty; the second removes archive_backup (created earlier in this section) along with everything inside it; the third removes a directory named temp_folder, and because of -f, does so without asking for confirmation even if temp_folder were not empty.

Viewing and Editing File Contents

Several commands display file contents without a full editor. cat prints an entire file at once and returns immediately to the prompt; -n numbers every printed line. Giving cat more than one filename displays them one after another as if they were a single file. head shows, by default, the first 10 lines of a file, and tail shows the last 10, useful for checking the start or end of a long file without printing all of it.

cat -n report.txt
[Output]
     1  Report for Lab 9
     2  Second finding

This prints the file's two lines back to the terminal, each preceded by its line number, because -n was given.

For files longer than one screen, less opens the file in a scrollable pager instead of dumping it all at once: the arrow keys, the space bar, or Ctrl+f move forward a page, Ctrl+b moves back a page, /word searches forward, n/N repeat that search forward/backward, and q exits.

To edit a file's contents, nano is a beginner-friendly editor that takes over the terminal the same way less does. nano filename edits an existing file or starts a new one. To save, press Ctrl+O (WriteOut), then Enter to confirm the filename. To leave, press Ctrl+X; if there are unsaved changes, nano asks whether to save them (Y saves and exits, N discards and exits, Ctrl+C cancels). Ctrl+G opens nano's built-in help.

flowchart TD
    A[Editing in nano] --> B[Press Ctrl+X to leave]
    B --> C{Unsaved changes?}
    C -- No --> G[Back at the shell prompt]
    C -- Yes --> D{Press Y, N, or Ctrl+C}
    D -- Y --> E[Save and exit]
    D -- N --> F[Discard changes and exit]
    D -- Ctrl+C --> H[Cancel, stay in nano]
    E --> G
    F --> G

Redirecting Output with echo

The echo command prints the text given to it on the terminal:

echo "Report for Lab 9"
[Output]
Report for Lab 9

This simply prints the given text back to the terminal, with nothing saved anywhere yet.

That same output can instead be sent into a file with a redirection operator. > writes into the named file, creating it if needed, and completely overwriting any previous content. >> instead appends to the end of the file, creating it only if it does not already exist:

echo "Report for Lab 9" > report.txt
echo "Second finding" >> report.txt

After these two lines, report.txt contains both sentences, one per line, because the second command appended rather than replaced. Running echo "New content" > report.txt a third time would erase both existing lines and leave only "New content" behind.

File Ownership and Permissions

Every file and directory has three categories of ownership: the owner (the user who created it and normally controls it), the group (a named set of users who share the same access), and others (everyone else, who typically get the least access). These are visible as the third and fourth columns of an ls -l listing:

ls -l report.txt
[Output]
-rw-r--r-- 1 student students 34 Oct 12 10:30 report.txt

Here the 1 right after the permission string is the link count, how many hard links point to this file, just one in this case; student is the owner and students is the group; everyone who is neither of those is treated as "others."

The first column, -rw-r--r--, is the permission string. Its first character is the file type (- file, d directory), and the remaining nine are three groups of three: owner, group, and others, in that order, each representing read, write, and execute, with - standing in for a permission not granted. Read opens a file or lists a directory's contents; write modifies a file or lets entries be created/removed inside a directory; execute runs a file as a program or lets a directory be entered with cd.

flowchart TD
    A[A user tries to access a file] --> B{Is the user the file's owner?}
    B -- Yes --> C[The owner permission triad applies]
    B -- No --> D{Is the user in the file's group?}
    D -- Yes --> E[The group permission triad applies]
    D -- No --> F[The others permission triad applies]

Only one triad ever applies to a given access attempt, decided in that fixed order: the owner triad applies whenever the user is the file's owner, even if that user also happens to belong to the file's group; group membership is only checked once ownership is ruled out.

Each permission also has a numeric value: read 4, write 2, execute 1, none 0. Adding the values for one triad gives a single octal digit, so rwx is 7, rw- is 6, r-x is 5, r-- is 4. Three digits together, one per triad, describe a file's full permissions; chmod 755 file sets owner to rwx, group to r-x, and others to r-x.

chmod (change mode) changes permissions in two styles.

Symbolic, using chmod [who][operation][permission] name, where who is u (owner), g (group), o (others), or a (all); operation is + (add), - (remove), or = (set exactly); and permission is any combination of r, w, x:

chmod u+x script.sh
chmod g-w report.txt
chmod u=rw,g=r,o= file.txt

The first line adds execute permission for the owner of script.sh; the second removes write permission from the group on report.txt. The third line shows that several who=permission clauses can be combined into one call by separating them with commas: it sets the owner to rw, the group to r, and, with nothing written after the last =, clears every permission for others.

Octal, using chmod NNN name with one digit per triad computed as above:

chmod 644 report.txt
chmod 755 script.sh
chmod 700 private_folder

The first line leaves report.txt as rw-r--r-- (owner can read and write, group and others can only read); the second leaves script.sh as rwxr-xr-x (owner has full access, group and others can read and execute); the third leaves private_folder as rwx------ (only the owner has any access at all).

Searching Text with grep

grep searches for a piece of text (a pattern) inside a file or inside another command's output, printing every matching line. -i makes the search ignore uppercase versus lowercase:

grep "Lab" report.txt
grep -i "lab" report.txt

The first line only matches lines containing the exact case Lab; the second, with -i, also matches lab, LAB, or any other capitalization.

grep becomes even more useful with the pipe operator |, which sends the output of the command on its left as input to the command on its right instead of printing it. Piping ls into grep filters a directory listing down to only matching entries:

ls -l | grep report

This runs ls -l as usual, but instead of showing every entry, only the lines containing "report" reach the terminal.

Archiving and Compression with tar, gzip, and zip

Archiving collects multiple files and directories into a single file without making them any smaller, purely to keep them together and easy to move as one unit, producing a .tar file. Compression instead reduces file size, using formats such as .gz or .zip.

tar (tape archive) creates, extracts, and lists archives: c creates, x extracts, t lists contents, f specifies the filename, and v (verbose) prints each file as it is processed:

tar cvf backup.tar file1.txt file2.txt documents/
tar tvf backup.tar
tar xvf backup.tar

These three lines demonstrate one archive in turn: the first packs file1.txt, file2.txt, and the documents directory into a new backup.tar, printing each name as it is added; the second lists everything stored inside backup.tar without extracting anything, leaving the current directory unchanged; the third extracts everything back out of backup.tar into the current directory.

gzip compresses a single file, appending .gz and, by default, deleting the original once done. gzip -d filename.gz (or gunzip filename.gz) reverses this and restores the original.

tar and gzip are commonly combined into one compressed archive, either as two steps or one command using tar's z option:

flowchart LR
    subgraph Two steps
        A1["tar cvf project.tar project/"] --> A2["gzip project.tar"] --> A3["project.tar.gz"]
    end
    subgraph One step
        B1["tar czf project.tar.gz project/"] --> A3
    end

Extracting that kind of archive in one step uses tar xzf project.tar.gz (x extract, z decompress, f filename).

zip builds a .zip archive, also compressed and readable on Windows. -r is required to include directories recursively:

zip -r backup.zip file1.txt documents/
unzip backup.zip
unzip -l backup.zip
unzip backup.zip -d /path/to/destination/

The first line creates backup.zip containing file1.txt and, recursively, everything inside documents/. unzip extracts a .zip archive; unzip -l lists its contents without extracting; unzip -d extracts into a chosen destination directory instead of the current one.

To compare an archive against the folder it was made from, du (disk usage) reports how much space a directory takes up. -s gives a single summary total instead of one line per file inside it, and -h prints that total in a human-readable form such as 4.0K or 2.3M, the same style -h produces for ls -lh:

du -sh documents

This prints one line: the total size of everything inside documents, followed by the directory name.

Practice Tasks

Task 1: Exploring the Shell, Getting Help, and Listing Files

  1. Open a terminal and run pwd. Expected output: your home directory path, for example /home/student.
  2. Run man ls. The manual page opens in the same scrolling viewer as less, so press q to close it and return to the prompt. Expected output: the manual page for ls opens and fills the terminal.
  3. Run ls --help. Expected output: a short options list printed directly, without a pager.
  4. Run cd /usr/share, then in turn ls, ls -a, ls -l, ls -lh, and ls -F. Expected output: ls shows plain names; -a adds ., .., and hidden dotfiles; -l shows one detailed line per entry (permissions, owner, group, size, date); -lh shows the same with sizes like 4.0K instead of raw bytes; -F appends / after every directory name (you may also see other markers such as @ or * on some entries; this task only covers the / marker).
  5. Type cd /usr/sh and press Tab. Expected output: the shell completes the path to /usr/share/.
  6. Run history, then history | grep ls. Expected output: numbered recent commands; the second run shows only lines containing "ls".

Task 2: Navigating and Organizing Directories

  1. Return to your home directory first with cd ~, then run cd /usr/share followed by pwd. Expected output: /usr/share.
  2. Run cd -. Expected output: prints your home directory path and moves back there.
  3. Run mkdir -p lab9_project/docs/reports. Expected output: no message, but lab9_project, lab9_project/docs, and lab9_project/docs/reports all now exist.
  4. Run cd lab9_project/docs/reports, then cd .., then cd .., then pwd. Expected output: the path now ends in lab9_project.
  5. Run mkdir empty_test then rmdir empty_test. Expected output: removed silently with no error, since it was empty.
  6. Run rmdir docs. Expected output: an error stating that the directory is not empty, since docs still contains reports.

Task 3: Creating, Copying, Moving, and Removing Files

  1. Inside lab9_project, run touch notes.txt draft.txt. Expected output: ls lists both new, empty files.
  2. Run cp -i notes.txt notes_backup.txt. Expected output: copied silently since notes_backup.txt did not exist; ls shows both files.
  3. Run cp -r docs docs_backup. Expected output: a new docs_backup directory appears, containing its own copy of reports.
  4. Run mv draft.txt docs_backup/. Expected output: draft.txt moves out of lab9_project and into docs_backup.
  5. Run rm notes_backup.txt. Expected output: deleted with no confirmation; no longer appears in ls.
  6. Run rm -r docs_backup. Expected output: docs_backup and everything inside it, including draft.txt, is removed.

Task 4: Viewing, Editing, and Redirecting Text

(Continuing inside lab9_project from the previous task; run cd ~/lab9_project if unsure.)

  1. Run echo "Report for Lab 9" > report.txt then cat report.txt. Expected output: the single line Report for Lab 9.
  2. Run echo "Second finding" >> report.txt then cat -n report.txt. Expected output: two numbered lines, 1 and 2, since >> appended rather than overwrote.
  3. Run echo "This replaces everything" > report.txt then cat report.txt. Expected output: only the new line appears; both earlier lines are gone because > overwrites.
  4. Run head /etc/services and tail /etc/services. Expected output: the first 10 lines, then the last 10 lines of the file.
  5. Run nano report.txt, add a line reading Lab 9 edited with nano, save with Ctrl+O then Enter, and exit with Ctrl+X. Expected output: back at the shell prompt; cat report.txt now shows the added line.
  6. Run less report.txt, scroll with the arrow keys, then press q. Expected output: contents shown a page at a time; the pager closes on q.

Task 5: Ownership, Permissions, and Searching

(Continuing inside lab9_project from the previous task; run cd ~/lab9_project if unsure.)

  1. Run ls -l report.txt. Expected output: a line such as -rw-r--r-- 1 student students 34 Oct 12 10:30 report.txt; identify the owner and group fields.
  2. Run chmod u+x report.txt then ls -l report.txt again. Expected output: the owner triad changes from rw- to rwx.
  3. Run chmod g-r report.txt then ls -l report.txt. Expected output: the group triad loses its r, becoming ---.
  4. Run chmod 644 report.txt then ls -l report.txt. Expected output: the permission string returns to -rw-r--r--.
  5. Run chmod 700 . then ls -ld . (the current directory is lab9_project, so . refers to it directly). Expected output: drwx------, giving only the owner any access.
  6. Run grep "Lab" report.txt. Expected output: the matching line is printed; a word not in the file prints nothing.
  7. Run ls -l | grep report. Expected output: only the report.txt line from the long listing, filtered out of the rest.

Task 6: Comprehensive Challenge - Archiving a Complete Project

Build the following project structure and archive it, using only commands from this lesson.

  1. Return to your home directory first: cd ~. Then create the directory structure in one command:
    mkdir -p final_project/{docs,scripts,data}
    Expected output: docs, scripts, and data, all created inside a new final_project directory.
  2. Populate each directory using echo redirection:
    echo "Project documentation" > final_project/docs/readme.txt
    echo "#!/bin/bash" > final_project/scripts/run.sh
    echo "echo 'Running project'" >> final_project/scripts/run.sh
    echo "id,value" > final_project/data/data.csv
    echo "1,100" >> final_project/data/data.csv
    Expected output: each file exists with the shown content, checked with cat.
  3. Set permissions: directories to 755, data and docs files to 644, the script to 755:
    chmod 755 final_project final_project/docs final_project/scripts final_project/data
    chmod 644 final_project/docs/readme.txt final_project/data/data.csv
    chmod 755 final_project/scripts/run.sh
    Expected output: ls -l inside each subdirectory shows the requested permission strings.
  4. Confirm with a search: run ls -l final_project/scripts | grep run.sh. Expected output: exactly one line, run.sh with rwxr-xr-x permissions.
  5. Create a single compressed archive of the whole project in one command:
    tar czf final_project.tar.gz final_project/
    Expected output: a new file final_project.tar.gz in the current directory.
  6. List the archive's contents without extracting it, then compare sizes:
    tar tvf final_project.tar.gz
    du -sh final_project
    ls -lh final_project.tar.gz
    Expected output: tar tvf lists every file and directory in the archive; the size commands show the compressed archive is close to or smaller than the original folder.
  7. Produce a .zip copy of the same project as a Windows-compatible archive:
    zip -r final_project.zip final_project/
    unzip -l final_project.zip
    Expected output: final_project.zip is created; unzip -l lists the same files as the tar archive, confirming both hold identical content.

Summary

This lesson covered the essentials of working as a regular Ubuntu user from the terminal. Every command follows command [options] [arguments], and man and --help always look up a command's exact behavior. pwd and cd (with ~, .., and -) move you around the filesystem, while ls and its -a, -l, -lh, -F, and -d options reveal what is in a directory, sped up by tab completion and command history.

Files and directories are managed with mkdir -p, touch, cp (-r for directories, -i to avoid overwrites), mv, rmdir, and rm (-r for directories, -f to skip confirmation, with no undo). Their contents are viewed with less, cat/cat -n, head, and tail, edited with nano (Ctrl+O to save, Ctrl+X to exit), and written directly with echo using > to overwrite and >> to append.

Every file carries three levels of ownership, owner, group, and others, visible in ls -l, and their read, write, and execute permissions change with chmod either symbolically (u, g, o, a with +, -, =) or numerically (4, 2, and 1 summed into a three-digit octal number). grep, alone or piped after ls, filters text down to what matters. Finally, tar bundles files together, gzip and zip shrink them, and the two combine into one compressed, portable archive of a project.