
Photo by Mark Stuckey on Unsplash
A lot of people who are told to learn Linux start with documentation, and a fair amount of them end up reading more documentation instead of doing anything. Docs describe systems; they don’t move files or run commands. If you’re new to Linux — because you’ve been handed an assignment, inherited someone’s machine, or decided the desktop is just too slow — your real problem isn’t vocabulary. It’s being able to act quickly and correctly on a live system without guessing.
The command line is where that action happens. Everything else in Linux is built on top of it: package managers issue commands through it, logs are read through it, backups run through it, and the tools you’ll actually touch day to day — file operations, text redirection, scripts — are shell constructs rather than features with friendly settings panels. So this guide is about the handful of shell behaviors that carry most sysadmin work, explained precisely enough that you can use them on a real machine today.
The file-management trio: touch, truncate, and redirection
Three commands handle more ordinary Linux tasks than almost anything else in the filesystem. They do overlapping jobs, which means beginners pick the wrong one — usually because they think of them as “create”, “delete”, and “edit.” They aren’t a create/delete/edit set at all.
| Task | Command | What it actually does |
|---|---|---|
| Create an empty file; update timestamps | touch |
Creates the file if missing, or updates its access/mtime metadata without changing content |
| Empty a file’s contents in place | truncate -s 0, or shell redirection to that file |
Sets the size directly, or writes nothing into it. Content is gone but the inode survives |
| Send output to a file; append to a file | > file / >> file |
Redirected stdout opens/truncates the target for writing, then streams input there |
The distinction that matters most: touch never touches your content, and redirection empties or appends it. If you want an empty log and intend to write into it right after, you’re about to hit a classic failure — see the caution below.
A useful way to remember truncate: it changes size without touching anything else about the file. It’s how you make an oversized backup smaller before transferring it, or clear the tail of a live log when the process that writes to it has already exited (in which case there’s nothing left to destroy). The GNU implementation supports -s SIZE and -d DATE; truncate on macOS exists too but only sets size with -s, so check your platform before assuming date support.
Redirection is the more dangerous one, because its power comes from being invisible: output meant for a terminal silently goes into a file instead. That’s also why it fails when redirected to itself. If you’re appending to app.log while app.sh writes to app.log, the redirect opens and empties app.log mid-write; everything already buffered is lost, and worse, the process may still be holding an open write handle that rewrites into a file whose descriptor now points elsewhere. The safe patterns are appending with >>, or copying first:
# Append to an existing log without disturbing current content
app.sh | tee >> app.log
cp /var/log/foo.raw /tmp/foo-backup.raw && tail -f /var/log/foo.raw | tee >> /var/log/foo.raw
tee is worth knowing here because it gives you the output too, which matters when you need to read what a command produced after saving it.
Passing text into commands with heredocs
Most of the time you’re not sending whole files — you’re sending multi-line text to something that expects it: grep, an editor, a test runner, a build tool, or another shell script. A single-argument command takes one line; a heredoc lets you pass many lines as one argument.
In Bash, this syntax puts the terminator on its own line and treats everything between it as input:
$ grep <<EOF
error
warning
EOF
The << marker means “from here until that terminator,” so multi-line input arrives intact. Two details cause most mistakes. First, plain heredocs expand variables in the passed text — write a script name into your heredoc and you’ll get an accidental variable expansion if the file is named $FOO. Second, indentation matters: Bash splits lines on whitespace boundaries unless you use the indented form <<-, which keeps tab-indented lines together as one argument.
# Indented heredocs preserve structure; unindented ones split at tabs
cat <<'EOF' > notes.txt
line one
line two
EOF
cat <<-'SCRIPT'
echo "hello"
SCRIPT
The quoted terminator, 'EOF', suppresses expansion entirely. That’s the form to use when you’re passing data rather than code — it stops a literal filename from being turned into a variable lookup, and it makes heredoc contents predictable for editors and parsers that don’t expect shell syntax inside them. The indented form with <<- is what you want when feeding source text to an editor or formatter.
One modern convenience: Bash 3.2+ accepts here-strings via <<<EOF instead of the older <<EOF. If your heredoc isn’t working on a recent shell, check whether it’s falling back for compatibility; newer syntax is generally fine. (Bash version details vary by distribution and OS, so confirm rather than assume.)
A practical habit: write multi-line commands in files instead of typing them into the terminal during interactive sessions. A heredoc makes that safe — you can compose the whole argument before anything runs — while cat piping from a file is simpler for large scripts but has no built-in protection against typos or expansion surprises.
Making repeatable work with Bash functions
Once you’re past one-off commands, the useful skill shifts: how do you stop re-running the same command with slightly different arguments? A function gives you that, and it’s just a labeled sequence of assignments plus actions in your own scope — nothing more:
backup_db() {
name="$1"
if [ "$name" = "" ]; then
echo "Usage: backup_db <dbname>" >&2
return 2
fi
pg_dump "$name" | gzip -c > "/tmp/backup-$name.sql.gz"
ls -lh "/tmp/backup-$name.sql.gz"
}
The $1 through $9 variables hold the first nine positional arguments; "$1" keeps spaces intact, whereas a bare $1 would turn my db into two separate words. [ ] is the shell’s test construct — it evaluates a condition and returns true or false, so the usage check above prints an error only when the argument list is empty. And return N ends the function with exit status N, which matters because that status propagates: if your function returns 1 on failure and you pipe it through something like set -e, the whole script stops there instead of continuing past a bad input.
Two cautions for functions specifically. Don’t declare variables at global scope inside a function when their value changes per call — in Bash that leaks into the surrounding shell, so use local declarations if the variable is meant to be temporary:
backup_db() {
local name="$1"
...
}
Second, functions are not portable across shells or machines by default. They live where you define them — a ~/.bashrc entry works on interactive Bash sessions, but if the same script runs with Bash under dash-like conditions (or another shell), that definition may never load. For genuinely reusable work, put it in an included file instead:
# Shared helper available to every invocation of my-script.sh
source /home/you/helpers.bash
Functions are right for ad-hoc sessions and quick local conveniences; sourced files are right when the same logic runs in more than one process or on more than one machine. Knowing which is which saves a debugging hour later.
How to learn Linux without burning out
The hardest part of learning any technical skill isn’t the material — it’s staying with it long enough to move from reading about something to doing it correctly. Documentation has an obvious failure mode here: you read a page, feel confident, close the tab, and then can’t reproduce the command on your own machine. That gap is where most self-taught learners stall.
A better rule for practice, and one worth adopting regardless of topic: test every piece of syntax you learn against something real before treating it as correct. A command that works in a sandbox tutorial may fail on the actual system because paths differ, permissions differ, or environment variables exist. This is why hands-on material outperforms documentation-heavy learning for sysadmin work — the failure case teaches more than the happy path ever could.
Concretely, make your first real targets small and recoverable: a scratch directory you can delete, not a production server; a VPS or container with disposable state rather than someone else’s machine; a virtual environment if that’s what you have. Small failures are cheap to diagnose and never cost anyone else’s data.
A useful sequence when starting out:
- Read the command reference for one behavior — say, file timestamps in
touch— and note its arguments exactly as documented. - Reproduce it on a scratch system, including the failure case (wrong permissions, wrong directory, oversized input).
- Write what you learned down in your own words or as a small script. If you can’t explain why something worked or failed, you haven’t actually learned it yet — only memorized the syntax.
- Apply it to a real problem, then go back and adjust based on what came back from the machine.
This loop is slower than skimming tutorials, which is precisely its advantage. It also makes your scripts better: writing them down forces you to decide argument order, quoting rules, and error handling before running anything at all — decisions that are expensive to revise once a script is in production.
That said, don’t overcomplicate the first pass. The commands above cover file creation, emptying, redirection, text input, and repeatable tasks; most everyday Linux work sits inside those five behaviors plus ls, cat, and process basics. Build confidence there before reaching for anything fancier, because scripting skills come from repetition against real constraints rather than from learning lots of syntax at once.
Where to go next
If the goal is a course rather than scattered practice, there are solid structured paths worth knowing about: TecMint runs tested Linux tutorials aimed at sysadmins — file management, Bash scripting, security hardening, and RHEL certification material — with an explicit rule that articles get checked on real machines before publication. Its scripting series in particular moves from variables to loops to cron jobs, which is the natural progression after this guide. On the book side, Techotopia’s “Ubuntu 20.04 Essentials” and “Rocky Linux 9 Essentials” libraries offer complete installation-and-administration references if you want a linear curriculum instead of individual articles; both are freely available through their respective TOC pages.
Linuxize is worth using for narrow reference questions — heredoc syntax, Nginx configuration behavior, SSH session handling — because its guides tend to focus on one command or config at a time with concrete examples rather than broad overviews. When you hit a specific problem, search by the exact symptom instead of browsing the whole site: “broken pipe,” “permission denied publickey,” or “location block” will find the relevant page faster than any general Linux overview.
The honest unknown here is always context-specific: your distribution, your shell version (Bash 4 vs 5 changes some heredoc and process-substitution behavior), and how your organization configures servers all affect what command form works where. Treat every example in this guide as correct for a typical modern Bash install on Ubuntu or Debian — and verify it against the system you actually have before relying on it in production.
Conclusion
The Linux command line rewards precise, repeatable habits rather than broad knowledge: use touch to create or refresh metadata, use redirection to move content, pass multi-line text through heredocs with expansion controlled deliberately, wrap repeated work in functions with clean argument handling and local variables, and test every new pattern on a real machine before trusting it. Learn that way and the shell stops being something you’re afraid of — it becomes the most flexible tool available for getting Linux systems up, fixed, documented, and automated.
FAQ
What’s the difference between touch, truncate, and shell redirection?
They overlap rather than form a create/delete/edit set: touch creates an empty file or refreshes its timestamps without changing content, while truncate -s 0 (or writing nothing into it) removes contents in place but leaves the inode alive. Redirection handles actual data — a single > opens and truncates the target before streaming input there, whereas >> appends to existing content.
How can I safely back up or clear a log that is still being written?
Appending with >> avoids destroying buffered entries, but redirecting output into an actively-opened file is unsafe because the shell empties the descriptor mid-write and may lose data already in flight. The reliable patterns are appending through tee, copying first (cp raw /tmp/backup && tail -f | tee >> backup), or using truncate -d only after confirming the writing process has exited.
How do I pass multi-line text to a command such as grep?
A heredoc lets you feed many lines as one argument by placing the terminator on its own line, for example:
$ grep <<EOF
error
war
EOF
This is useful for sending config blocks, test cases, or script content to tools that accept a single multiline input; just remember EOF must be alone on its terminating line.
Why do heredocs and redirection behave differently in scripts?
Redirection happens at parse time when the shell opens the file descriptor, so it can corrupt files being written by other processes and fails when redirected into itself because the target is emptied before the command writes to it. A heredoc stays open until its terminator line arrives, which makes it safer for feeding multiline input without disturbing an existing file or losing buffered output.
Related Articles
- Mastering Linux: The Essential Toolkit for Resource Monitoring (50+ Commands with Examples)
- Getting Started with Docker: A Beginner’s Guide to Containerization – Master Modern Infrastructure Today
- Linux System Metrics That Matter: CPU, Memory, Disk I/O — What to Watch Daily | Essential Monitoring Guide for Modern Servers


