Write Your First Shell Script
Write your first shell script in this Linux · networking · telemetry tutorial — step-by-step guide for developers.
Focus: Write Your First Shell Script
You've been typing the same sequence of commands into your terminal over and over—maybe to check disk usage on a server, restart a service, or parse a log file. Each time you copy-paste from a chat window or scroll through your shell history, you risk typos, wasted time, and inconsistent results. That repetition is the pain this lesson solves: you'll learn to write your first shell script so that a single command can execute a whole series of actions reliably, faster, and with zero memory lapses. By the end, you'll have a working script, a mental model for how it runs, and the confidence to build more complex automation on your path to platform fluency.
The problem this lesson solves
Manual terminal work doesn't scale. Whether you're a backend developer juggling microservices or a DevOps engineer responsible for multiple Linux hosts, repeating the same commands invites human error and wasted effort. Here's what typically goes wrong:
- You misremember a flag (is it
-for-F?) and get an unexpected output. - You forget a step in a multi-command sequence (like checking disk space before deploying).
- You lose time hunting through
historyfor that one command you used last week. - You can't reproduce a workflow reliably for a colleague or a CI pipeline.
A shell script transforms a fragile, memory-dependent process into a single, documented, executable file. It's the difference between giving someone a list of ingredients and giving them a recipe you've already tested. For anyone in the Linux · networking · telemetry track, scripts are also the glue that connects commands: you'll use them to run curl probes, parse JSON responses, and trigger telemetry collection.
Core concept / mental model
Think of a shell script as a list of commands that the shell reads line by line, just as if you'd typed them. That's the whole mental model. There's no magic—no compilation, no new language. You're writing plain text that the Bash interpreter executes sequentially (unless you add logic like loops or conditionals).
Here's a diagram of the flow:
Your script (text file)
│
▼
Shell interpreter (e.g., Bash)
│
▼
Executes commands in order (with inputs/outputs)
│
▼
Returns exit codes & output to your terminal
Key terms to know:
- Shebang (
#!/bin/bash): the first line that tells the system which interpreter to use. Without it, your script might run with the wrong shell or not at all. - Executable bit: a file permission that allows the system to run the script like any other program. You set it with
chmod +x. - Exit status: a number (0 for success, non-zero for error) that the script returns when it finishes. You can use it to detect failures and chain scripts together.
Anchor this idea: a script is just a saved sequence of commands with a bit of structure. Once you hold that, everything else (variables, conditionals, loops) is just layering on top.
How it works step by step
Writing your first shell script is a five-step ritual. Repeat it until it's muscle memory:
- Create a file with a
.shextension (optional but conventional), e.g.,myfirst.sh. - Add the shebang as the first line:
#!/bin/bash. This ensures your script runs with Bash, not another shell. - Write your commands on separate lines. Each line is a command exactly as you'd type it in the terminal.
- Make it executable: run
chmod +x myfirst.shin your terminal. This flips the execute bit. - Run it: execute with
./myfirst.shfrom the directory (the./tells the system to look in the current directory).
Cause → effect: without the shebang, the system may use your default shell (which might be sh or zsh), potentially causing subtle differences. Without the executable bit, ./myfirst.sh fails with "Permission denied." Without ./, the shell won't find your script unless it's in your PATH.
Hands-on walkthrough
Let's build a script that prints system info and a network check—the kind of thing you'll want on any Linux box. Create a file called syscheck.sh with your favorite editor:
#!/bin/bash
# My first script: print system info and ping a host
echo "System info:"
uname -a
echo ""
echo "Disk usage:"
df -h /tmp
echo ""
echo "Ping 8.8.8.8 (4 packets):"
ping -c 4 8.8.8.8
Save it, then make it executable and run it:
chmod +x syscheck.sh
./syscheck.sh
Expected output (trimmed):
System info:
Linux myserver 5.15.0-91-generic #101-Ubuntu SMP ... x86_64 ...
Disk usage:
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 20G 30G 41% /tmp
Ping 8.8.8.8 (4 packets):
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=114 time=12.3 ms
...
Now let's make it more practical with variables and an exit status. You'll often want to know if the network check failed. Here's an improved version:
#!/bin/bash
SERVER="8.8.8.8"
PING_COUNT=4
echo "Pinging $SERVER ($PING_COUNT times)..."
ping -c "$PING_COUNT" "$SERVER" > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "Success: $SERVER is reachable."
exit 0
else
echo "Failure: $SERVER is unreachable." >&2
exit 1
fi
Run it, then test the failure path by changing SERVER to an unreachable address like 192.0.2.1 (a reserved, non-routable IP) and run again:
./syscheck.sh # works, exit 0
# Edit SERVER="192.0.2.1" and run again
./syscheck.sh # fails, exit 1
You'll see the failure message, and echo $? in your terminal will confirm 1. This pattern—checking a command's exit status—is the foundation for robust scripts.
For a third example, here's a script that loops through a list of hosts and pings each one. This mimics a simple health check you might run before deploying:
#!/bin/bash
HOSTS="google.com github.com example.com"
for host in $HOSTS; do
echo "--- Checking $host ---"
ping -c 1 "$host" > /dev/null 2>&1 && echo "OK" || echo "FAIL"
done
When you run it, you'll see each host's status on its own line—a tiny telemetry dashboard in your terminal.
Compare options / when to choose what
You might wonder: why not just use bash -c "..." in the terminal, or use a one-liner? For tiny tasks, a one-liner is fine. But for anything you might run more than once, a script is better. Here's a comparison:
| Option | Use case | Pros | Cons |
|---|---|---|---|
Inline command (&& chains) |
Quick checks, one-off tasks | No file needed, fast | Hard to read, no persistence |
| Shell script | Repeated tasks, automation, sharing | Documented, reusable, versionable | Requires file management |
Functions (in .bashrc) |
Interactive shell shortcuts | Loaded automatically | Less portable across machines |
| Scripting languages (Python, Perl) | Complex logic, data processing | More powerful, expressive | Heavier, slower startup |
For most system administration and telemetry tasks, a Bash script is the sweet spot. It's the lingua franca of Linux—present on virtually every server—and it integrates seamlessly with commands like grep, awk, and curl. Choose a scripting language when you need complex data structures or JSON parsing beyond jq's comfort zone; but for orchestrating commands, Bash wins.
Variations
- Use
#!/usr/bin/env bashinstead of#!/bin/bashto allow the system to locate Bash via thePATH—more portable across distributions. - Make your script accept arguments with
$1,$2, etc., so it's reusable for different hosts or paths. - Add
set -enear the top to make your script exit immediately on any error, a common practice in production scripts to avoid cascading failures.
Troubleshooting & edge cases
Permission deniedwhen running./script.sh→ Usechmod +x script.sh. If the file is on a mounted volume withnoexec, move it or run it withbash script.sh(bypasses the executable bit, but the shebang is ignored).command not found→ Check the first line: if the shebang points to#!/bin/bashand that path doesn't exist (e.g., in some minimal containers), install Bash or change to#!/bin/sh. Also verify you typed./before the filename.- Script runs but output is wrong → Use
bash -x script.shto trace each command as it executes. This prints+lines showing expansions, which reveals quoting or variable issues. - Variables with spaces → Always quote variables in commands:
ping -c "$COUNT" "$HOST". Without quotes, a variable containing spaces (like "my host name") gets split into multiple arguments. - Exit status not 0 even though commands succeeded → The script's exit status is that of the last command. If your final
echosucceeds, the exit is 0 even if apingearlier failed. Useexitexplicitly to control the status. - Windows line endings → If you edit on Windows and transfer,
\r\ncauses$'\r': command not found. Convert withsed -i 's/\r$//' script.shor use a Linux editor.
What you learned & what's next
You've now written your first shell script, made it executable, and understood the essentials: the shebang, the executable bit, variables, exit statuses, and basic control flow. These are the same building blocks used in production scripts that monitor servers, parse logs, and trigger telemetry collection—all part of the Linux · networking · telemetry track.
You're ready to move on to the next lesson: shell variables and expansion—where you'll learn how to capture command output into variables ($(command)) and write scripts that adapt to their environment. With that, your scripts will become truly dynamic, and you'll be one step closer to automating your entire platform.
Pro tip: Keep a dedicated
~/binfolder for your scripts and add it to yourPATH. Then you can runsyscheckfrom anywhere—just like a native command. This is a small habit that pays off every day.
Practice recap
Create a script called healthcheck.sh that checks disk usage of /tmp and pings 8.8.8.8. If the ping succeeds, print 'Network OK'; otherwise, print 'Network FAIL' and exit with status 1. Run it, then modify it to accept the host as a positional argument ($1) and test with both reachable and unreachable addresses.
Common mistakes
- Forgetting to make the script executable with
chmod +x, then running./script.shand getting 'Permission denied'—remember the executable bit is separate from your read/write permissions. - Using
#!/bin/shinstead of#!/bin/bashwhen you rely on Bash-specific features like arrays or[[ ]]—you'll get cryptic syntax errors on some systems. - Not quoting variables, e.g.,
ping -c $COUNT $HOST—if a variable contains spaces or is empty, the command may behave unexpectedly or throw errors. - Assuming the script's exit code is 0 even if a command fails because the script doesn't use
set -eor an explicitexit—the exit status is always from the last command run. - Using Windows-style line endings after editing on Windows or Mac—you'll see
\rerrors; runsed -i 's/\r$//' script.shto fix.
Variations
- Use
#!/usr/bin/env bashas shebang for better portability across Unix-like systems where Bash lives in different paths. - Make your script accept positional parameters (
$1,$2) so you can pass hosts or filenames without editing the file each time. - Add
set -enear the top to cause the script to exit immediately on any failing command—a common practice for production scripts to avoid cascading errors.
Real-world use cases
- A script that performs a series of system health checks (disk, memory, network) and emails a report—scheduled via cron.
- A deployment helper that stops a service, pulls new code, runs tests, and restarts it—ensuring a consistent, repeatable process.
- A telemetry collector that pings multiple endpoints, parses response with
curlandjq, and writes metrics to a log file.
Key takeaways
- A shell script is just a sequence of commands saved in a file—the shell executes it line by line.
- The shebang (
#!/bin/bash) selects the interpreter, and the executable bit (chmod +x) makes it runnable. - Use variables, conditionals, and exit statuses to build robust, dynamic scripts.
- Always quote variables and test edge cases (missing args, unreachable hosts) to avoid silent failures.
- The exit code of the last command determines the script's exit code unless you explicitly
exit. - Scripts are the foundation for automating Linux, networking, and telemetry tasks—master them to advance in the track.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.