Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
FREEDOM251

Freedom251 › Blog › Automation

Shell Scripting for Beginners: Automate Tasks on a Linux Server

By Freedom251 Team · · 12 min read

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The practical path is simple: run a Linux command successfully by hand, place the command in a Bash script, add arguments and checks, test its failure paths, and only then schedule it. A script turns repeatable administration into an inspectable file that can be reviewed, logged, rerun, and improved.

What a shell script is and why it helps on a server

Bash is both the program that interprets commands and a programming language for combining them. The GNU Bash Reference Manual describes Bash as “the shell, or command language interpreter, for the GNU operating system” and notes that “a Unix shell is both a command interpreter and a programming language.” That combination lets one file call utilities, make decisions, repeat work, transform output, and return a success or failure status.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

“A shell script is a text file containing shell commands.” Bash reads that file non-interactively, so it can perform the same sequence without you typing each command. A script can receive positional arguments such as $1 and $2, and it can be made directly executable by adding the execute permission with chmod.

Interactive and non-interactive execution

When you type at a terminal, you see prompts, environment variables, aliases, your current directory, and immediate error messages. A scheduled script has none of those guarantees. It runs with the account that owns the schedule and with an environment supplied by the scheduler. Designing for that difference—by using explicit paths, quoted variables, and deliberate logging—is the foundation of reliable server automation.

The shebang chooses the interpreter

Put an interpreter directive on the first line:

#!/usr/bin/env bash

This asks the system to find Bash through env. Bash-only features such as [[ ... ]], arrays, and certain parameter expansions should not be placed in a script whose shebang says sh. If a script is intended to be POSIX shell code, use a POSIX-compatible shebang and avoid Bash-specific syntax.

How do I write my first Bash script?

1. Create a harmless, user-owned task

Start with a directory you control rather than a system service or a production data path. The following script creates a dated report directory and writes a short status file:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#!/usr/bin/env bash

report_root="$HOME/reports"
stamp=$(date +%Y-%m-%d)
report_dir="$report_root/$stamp"

if ! mkdir -p -- "$report_dir"; then
    printf 'Could not create %sn' "$report_dir" >&2
    exit 1
fi

printf 'Report created at %sn' "$(date --iso-8601=seconds)" > "$report_dir/status.txt"
printf 'Wrote %sn' "$report_dir/status.txt"

Save it as make-report.sh. The variable assignments make the paths visible and easy to change. Quoting every expansion prevents spaces or wildcard characters in a path from being interpreted as separate words or filename patterns.

2. Check syntax, set the permission, and run it

  1. Check the file without running its commands: bash -n make-report.sh.
  2. Add the execute bit: chmod +x make-report.sh.
  3. Run it from the directory containing the file: ./make-report.sh.
  4. Inspect the result and the status code: printf '%sn' "$?".

A zero exit status conventionally means success; a nonzero value signals failure. Do not assume that seeing some output means every operation succeeded. Check the status of commands whose failure would make the result unusable.

3. Accept arguments and validate them

Arguments make a script reusable without editing its source. $1 is the first argument, $2 the second, and "$@" expands to all arguments as separate, safely quoted words. Check required inputs before changing anything:

#!/usr/bin/env bash

if (( $# != 2 )); then
    printf 'Usage: %s SOURCE_DIR DEST_DIRn' "$0" >&2
    exit 2
fi

source_dir=$1
dest_dir=$2

if [[ ! -d $source_dir ]]; then
    printf 'Source is not a directory: %sn' "$source_dir" >&2
    exit 1
fi

printf 'Would process %s into %sn' "$source_dir" "$dest_dir"

$0 is the script name. An explicit usage message and a distinct argument-error status help both people and schedulers diagnose a bad invocation.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The Bash building blocks to learn first

The Bash reference groups the essentials into syntax, commands, functions, parameters, expansions, redirections, and script execution. Learn them in that order: first understand how text becomes arguments, then add decisions and repetition.

Quoting and expansions

  • Double quotes: "$path" expand variables while preserving embedded spaces. Use them around almost every variable used as a pathname or argument.
  • Single quotes: 'literal $text' prevent variable and command expansion.
  • Command substitution: today=$(date +%F) captures a command’s standard output. It is clearer and safer than legacy backticks.
  • Arithmetic expansion: next=$((count + 1)) performs integer arithmetic.
  • Globbing: *.log is expanded by the shell to matching names. An unmatched pattern may remain literal, so test the resulting names before destructive operations.

Unquoted command substitutions and variables are a frequent source of bugs because the shell performs word splitting and pathname expansion on their results. Deliberately leave a value unquoted only when you understand and want that behavior.

Conditionals and tests

Bash’s if executes a branch based on a command’s exit status. The double-bracket form is Bash syntax and avoids several parsing surprises:

if [[ -r $config_file ]]; then
    printf 'Reading %sn' "$config_file"
elif [[ -e $config_file ]]; then
    printf 'File exists but is not readablen' >&2
    exit 1
else
    printf 'Missing: %sn' "$config_file" >&2
    exit 1
fi

Useful tests include -e (exists), -d (directory), -f (regular file), -r (readable), and numeric comparisons such as -gt. Use (( ... )) for arithmetic conditions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Loops

Use a for loop for a known set of words or files and a while loop when work continues until a condition changes:

for file in "$report_dir"/*.txt; do
    [[ -f $file ]] || continue
    printf 'Found %sn' "$file"
done

while IFS= read -r line; do
    printf 'Line: %sn' "$line"
done < input.txt

The IFS= read -r form preserves leading whitespace and backslashes while reading lines. Never feed filenames through an unquoted for file in $(...); newline and space characters will split names incorrectly.

Functions, pipelines, and redirection

Functions package a repeated operation and make the main flow easier to read:

log() {
    printf '%s %sn' "$(date --iso-8601=seconds)" "$*"
}

log 'starting job'

A pipeline sends one command’s standard output to the next command’s standard input, for example journalctl --since yesterday | grep -F 'error' | wc -l. Redirection writes or reads streams: > replaces a file, >> appends, 2> redirects standard error, and 2>&1 combines standard error with standard output. In a scheduled job, redirect both streams to a deliberate log or monitoring destination.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Exit status and command chains

$? contains the status of the most recently completed command, so capture or test it immediately if it matters:

if tar -czf "$archive" -C "$parent" "$name"; then
    printf 'Created %sn' "$archive"
else
    printf 'Archive failedn' >&2
    exit 1
fi

command1 && command2 runs the second command only after success; command1 || command2 runs it after failure. These operators are useful for short, obvious checks, but use a full if block when the failure response deserves explanation or cleanup.

Safe defaults, validation, and testing

Syntax checking is not execution testing

bash -n script.sh asks Bash to parse the file without executing its commands. POSIX documents the equivalent set -n behavior as reading commands without executing them for syntax checking. This catches missing quotes, parentheses, and other grammar errors, but it cannot tell you whether a path exists, a permission is sufficient, or a command produces the expected data.

Understand the limits of set -e

set -e requests that Bash exit when a simple command fails, but POSIX specifies exceptions. Failures used as the test in if, while, or until, commands in some && and || lists, and parts of pipelines can be exempt. Treat it as one tool, not a complete error-handling design. Check important commands explicitly and test expected failure paths.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

set -u can expose accidental use of unset variables, but it also requires you to provide defaults such as ${OPTIONAL:-}. A pipeline’s status can hide an earlier failure unless you deliberately account for pipeline behavior; many Bash practitioners study pipefail alongside set -e and set -u. Add these settings only after understanding how your script handles optional values and intentional nonzero statuses.

Use a linter as advice, not proof

ShellCheck can point out quoting mistakes, suspicious tests, and portability problems. Review each warning in context: a clean lint result does not prove that the script has correct permissions, safe data boundaries, or an appropriate recovery path.

A practical test checklist

  • Run with a temporary input directory and a destination you can delete.
  • Test missing arguments, nonexistent paths, unreadable files, and a destination that cannot be written.
  • Test filenames containing spaces, tabs, and shell metacharacters.
  • Run twice to see whether the result is safely repeatable or creates duplicate damage.
  • Record the intended exit status for success and each important failure.
  • Only after interactive tests pass, run the exact command under the account and working directory that will own the schedule.

Server-oriented automation examples

Back up a configuration directory with a dated archive

This pattern is intentionally explicit. Replace the example paths, account, and retention policy with values appropriate to your server, and use a source and destination that the executing user can read and write:

#!/usr/bin/env bash

if (( $# != 2 )); then
    printf 'Usage: %s SOURCE_DIR DEST_DIRn' "$0" >&2
    exit 2
fi

source_dir=$1
dest_dir=$2

[[ -d $source_dir ]] || { printf 'Missing source: %sn' "$source_dir" >&2; exit 1; }
mkdir -p -- "$dest_dir" || exit 1

stamp=$(date +%Y%m%d-%H%M%S)
base=$(basename -- "$source_dir")
parent=$(dirname -- "$source_dir")
archive="$dest_dir/${base}-${stamp}.tar.gz"

tar -czf "$archive" -C "$parent" "$base" || {
    printf 'Backup failed: %sn' "$archive" >&2
    exit 1
}

printf 'Backup created: %sn' "$archive"

The -C option keeps the archive’s stored paths relative to the source’s parent. Verify the archive with the appropriate listing or extraction test before treating it as a backup. A successful archive command is not a guarantee that a later restore will meet your recovery needs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Write a disk-usage report

For a report job, choose an explicit filesystem path and retain the output where the scheduled user can write. Use command -v du, date, and other commands interactively to discover their locations before hard-coding absolute paths for cron. Keep threshold checks separate from report generation so an alert condition cannot silently suppress the report.

Remove old files only after a dry run

Retention jobs are irreversible unless a backup exists. First list candidates with a command such as find /path/to/cache -type f -mtime +30 -print. Review that list, then add deletion only when the path, file type, and age rule are correct. Keep the directory in a configuration variable, refuse an empty or unexpected value, and log the number or names of files considered. Never concatenate unvalidated input into a destructive command.

Design for operations, not just syntax

  • Absolute paths: do not depend on the caller’s current directory or interactive PATH.
  • Least privilege: run as the owning user or a dedicated service account; use root only for operations that genuinely require it.
  • Explicit configuration: put source paths, destinations, retention days, and service names near the top or pass them as validated arguments.
  • Repeatability: make a second run safe, or detect an existing result before changing anything.
  • Overlap protection: use an atomic lock directory or another locking mechanism when a run must not overlap with itself.
  • Logging: record start, finish, important decisions, and failures. Include enough context to identify the host, account, and paths without exposing secrets.
  • Secrets: do not place passwords or tokens directly in a script or command line; use the server’s approved secret mechanism.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How do I run a shell script every day?

Understand the crontab model

The Linux crontab(5) documentation describes a crontab as instructions for the cron daemon in the simplified form “run this command at this time on this date.” Each user can have a crontab, and commands run with that user’s permissions. A typical entry has five time and date fields followed by the command:

Field Allowed values Meaning
Minute 0–59 Minute of the hour
Hour 0–23 Hour in local server time
Day of month 1–31 Calendar day
Month 1–12 Calendar month
Day of week 0–7 Sunday is commonly 0 or 7

Use commas for alternatives, hyphens for ranges, and a slash for intervals. An asterisk means every permitted value.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Install a daily user job

  1. Run the script manually with the exact arguments you intend to schedule.
  2. Confirm the script is executable: chmod +x /home/alex/bin/backup-config.sh.
  3. Open the current user’s crontab: crontab -e.
  4. Add an entry such as 0 2 * * * /home/alex/bin/backup-config.sh /home/alex/myapp/config /home/alex/backups >>/home/alex/logs/backup.log 2>&1.
  5. Save and exit, then verify installation with crontab -l.
  6. Check the redirected log and the destination after the scheduled time; do not assume that an installed entry has run successfully.

This example runs at 02:00 according to the server’s local clock. Create the log directory first, and ensure the crontab owner can write both the log and backup destination.

Why does my script work in the terminal but fail in cron?

Cron supplies a limited environment and normally interprets the command with /bin/sh unless the crontab sets SHELL. Your interactive shell may provide aliases, functions, a larger PATH, a different working directory, and variables that cron does not. Use a Bash shebang for Bash syntax, quote paths, set required variables in the script, and call external programs by known absolute paths when predictable behavior matters. Capture both standard output and standard error while diagnosing.

Do not put a cd assumption in a script. Derive paths from explicit configuration or from the script’s own location when appropriate. If the job depends on a mounted filesystem, network credentials, or a service being ready, test those prerequisites and fail with a useful message.

Calendar edge cases

Local-time schedules interact with daylight-saving changes. The Linux crontab documentation notes that nonexistent local times do not match and repeated times can run twice. Avoid scheduling a critical one-time action at an ambiguous transition, and make the script safe if the scheduler invokes it twice.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choosing a trigger and operating model

Axis Manual or on-demand script Cron calendar job Service or timer manager
Trigger A person or another command starts it. Five calendar fields determine when it starts. A service manager can express calendar or boot/event triggers.
Environment control Usually the fullest interactive environment. Daemon-provided, limited environment; normally /bin/sh for the crontab command. Defined unit properties, dependencies, and environment files.
Failure visibility Terminal status and output. Redirected logs or configured mail; visibility must be designed. Often integrated with the service manager’s journal and status commands.
Privilege and blast radius Whichever account invokes it. The crontab owner’s account. A unit’s configured service account, sometimes root.
Repeatability Depends on the operator. Requires idempotence and overlap protection. Can provide dependency ordering and restart policies, but the script still needs safe behavior.
Portability Can target POSIX sh or Bash. Portable scheduling syntax, with implementation differences. More platform-specific configuration.

Cron is a good first scheduler for a straightforward daily or hourly job. Move to a service or timer manager when you need dependency ordering, stronger status reporting, event triggers, or centralized journal integration. The script’s input validation, permissions, logging, and locking remain necessary under any scheduler.

A learning path that builds useful skill quickly

  1. Commands and streams: learn command options, standard input/output/error, pipelines, redirection, and exit status.
  2. Quoting and parameters: practice variables, positional arguments, command substitution, globbing, and safe filename handling.
  3. Control flow: write small conditionals, loops, and functions that each do one job.
  4. Validation: use bash -n, review ShellCheck advice, and test both success and failure paths.
  5. Operations: add logging, least-privilege execution, idempotence, locking, and explicit configuration.
  6. Scheduling: install a user crontab only after the exact command succeeds interactively, then inspect logs and outputs after the first run.

A Linux command-line book or a focused Bash scripting reference can provide exercises beyond these examples. The important progression is to keep each script small enough to inspect, make its assumptions visible, and automate only after you can explain what happens when every important command succeeds or fails.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

AutomationBashCronLinux administrationLinux serverShell Scripting

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.