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

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Bash (the Bourne-Again SHell) is both the command interpreter you use at a Linux terminal and a programming language for automating server work. It reads commands, expands variables and patterns, applies redirections and pipelines, runs built-in or external commands, and returns an exit status that scripts can test. This guide explains that process, shows the Bash features that matter on servers, and helps you start writing reliable scripts without confusing Bash with the more limited POSIX sh shell.

What Bash is

GNU describes Bash as “the shell, or command language interpreter, for the GNU operating system.” The name is a pun on Bourne shell: Bash is a successor to the original Unix sh shell associated with Stephen Bourne. It remains largely compatible with sh, incorporates useful ideas from the Korn and C shells, and is designed to conform to the POSIX Shell and Tools part of IEEE Standard 1003.1.

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

A shell has two closely related jobs:

  • Command interpreter: it accepts text from a terminal or another input source, decides what that text means, and starts the requested commands.
  • Shell programming language: it provides variables, parameters, conditions, loops, functions, quoting, redirections, and other structures for reusable programs called shell scripts.

Bash can run interactively while you administer a server, or non-interactively from a script, a scheduled job, a deployment system, or a command string. It is portable across nearly every Unix version and some other operating systems, although the installed version and the shell selected by a distribution can differ.

Why Bash matters on a Linux server

Most server administration starts with small commands: inspect a directory, view a log, check a service, copy a file, or query a process. Bash gives those commands a consistent environment and lets you combine them into repeatable operations. A pipeline can pass one command’s output to another; a redirection can save output to a file; a loop can process many hosts or files; and a conditional can stop a deployment when a check fails.

Bash is especially useful when the work is primarily orchestration. It can call programs such as package managers, service managers, archivers, database clients, and network tools, then make decisions from their output and exit statuses. It is not automatically the best choice for every application: large, stateful programs, complex data parsing, and strict cross-platform portability may be better handled by another language. For short-to-medium administrative tasks that already use Unix utilities, Bash keeps the glue close to the commands.

How Bash processes a command

Understanding the command lifecycle explains many surprising results. When you type a line, Bash does not simply split it at spaces and run it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Read: Bash obtains a line from the terminal, a script, or another input source.
  2. Parse: it recognizes words, operators, pipelines, lists, quoted regions, assignments, and control structures according to shell grammar.
  3. Expand: it performs operations such as parameter expansion ($name), command substitution ($(command)), arithmetic expansion ($((expression))), tilde expansion, and filename expansion (globbing with patterns such as *.log).
  4. Apply redirections: operators such as >, >>, <, and 2> connect file descriptors before the command starts.
  5. Run: Bash executes a shell built-in, a function, an external program, or a command found through the PATH search.
  6. Set a status: the command produces an exit status. By convention, 0 means success and a nonzero value means some kind of failure. The value is available in $? until another command changes it.

Quoting controls the expansion stage. Double quotes preserve spaces while still allowing parameter and command expansion; single quotes preserve the characters literally; and an unquoted expansion can be split into multiple words and then subjected to filename expansion. When a value may contain spaces, newlines, wildcard characters, or user input, quote it deliberately.

Shell built-ins versus external programs

Some commands are implemented inside Bash. Examples include cd, break, continue, read, export, return, and exec. A built-in can change the current shell’s state: cd changes the shell’s working directory, and export changes which variables child processes inherit.

Other commands are separate executable files, usually located in directories listed by PATH. Commands such as ls, grep, find, and tar are normally external programs. Use these checks when you are unsure what Bash will run:

type cd
type -a ls
command -v tar

type can reveal an alias, function, built-in, or executable. This matters when a script behaves differently from an interactive session that has aliases or functions configured.

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

Start with Bash on your server

Check the installed version

The GNU Bash Reference Manual is Edition 5.3, updated 18 May 2025, but your server may ship an older or vendor-patched package. Check the actual interpreter instead of assuming the manual version matches:

bash --version
printf 'Bash major version: %sn' "${BASH_VERSINFO[0]}"

The first command prints the complete version string. The second uses Bash’s version array and therefore must itself be run by Bash.

Find the shell you are actually using

Several different values can be relevant:

printf 'Current process: %sn' "$0"
printf 'Login shell in account database: %sn' "$SHELL"
ps -p $$ -o pid=,comm=,args=

$0 describes how the current shell was invoked, while $SHELL commonly reflects the account’s configured login shell and is not a guarantee about every process. The ps command shows the running process. A script’s interpreter is determined by its shebang, not by whichever shell happens to be open in your terminal.

Run your first commands

pwd
printf 'Files in this directory:n'
ls -la
cd /var/log
printf 'Now in: %sn' "$PWD"

pwd prints the current directory, ls -la lists entries including hidden ones, and cd is a built-in that changes Bash’s own working directory. Prefer printf for predictable formatting in scripts; unlike some implementations of echo, it does not have ambiguous handling of escape sequences and options.

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

The Bash building blocks you will use most

Variables and parameters

Assignment has no spaces around the equals sign. Read a variable by prefixing its name with $, and quote the expansion when it represents one value:

backup_dir="/srv/backups"
file_count=0
printf 'Backups go to %sn' "$backup_dir"
((file_count += 1))

Names beginning with an uppercase letter are not inherently special; consistent naming is more important than capitalization. Environment variables are variables exported to child processes:

export APP_ENV=production
bash -c 'printf "Child sees: %sn" "$APP_ENV"'

Special parameters provide information about a script invocation. $1, $2, and so on are positional arguments; $# is the argument count; $@ represents all arguments; and $? is the most recent exit status. In a loop, use "$@" to preserve each argument as a separate item:

for path in "$@"; do
    printf 'Checking %sn' "$path"
done

Quoting and safe expansion

These commands are not equivalent:

name='weekly report.txt'
printf '%sn' $name
printf '%sn' "$name"

The unquoted expansion can be split into two words. The quoted expansion remains one argument. Quote variable expansions by default, then remove the quotes only when you intentionally need word splitting or filename matching.

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.

Pipelines

The pipe operator sends standard output from one command to the standard input of the next:

grep -F 'ERROR' /var/log/app.log | tail -n 20

This asks grep to select matching lines and tail to keep the last 20. A pipeline connects processes; it does not make the commands share variables or run in the same shell context by default. For scripts that must notice a failure in any pipeline element, Bash provides pipefail:

set -o pipefail
if grep -F 'ERROR' /var/log/app.log | tail -n 20 > errors.txt; then
    printf 'Pipeline completed successfullyn'
fi

Redirection and file descriptors

Every process normally has standard input (file descriptor 0), standard output (1), and standard error (2). Redirections attach those streams to files or other descriptors:

command > output.txt       # replace standard output
command >> output.txt      # append standard output
command 2> error.txt       # replace standard error
command >all.txt 2>&1      # send both streams to one file
command < input.txt        # read standard input from a file

The order of redirections matters. command >all.txt 2>&1 first points standard output at the file and then duplicates that destination for standard error. In Bash, &>all.txt is a shorter equivalent for both streams, but the longer form is more recognizable in scripts intended for other POSIX shells.

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.

Exit statuses

Test a command immediately if its result matters:

if systemctl is-active --quiet ssh; then
    printf 'The service is activen'
else
    status=$?
    printf 'Service check failed with status %sn' "$status" >&2
fi

Do not run another command before capturing $?; even printf would replace it. Use && when the second command should run only after success, and || for an alternative path:

mkdir -p /srv/app && printf 'Directory is readyn' || printf 'Could not create directoryn' >&2

Write and run an executable Bash script

1. Create a file with an explicit interpreter

#!/usr/bin/env bash

set -u
printf 'Running as Bash %sn' "${BASH_VERSION}"
printf 'First argument: %sn' "${1-}"

The first line is the shebang. /usr/bin/env bash searches PATH for Bash, while a fixed path such as /bin/bash may be preferable in a tightly controlled image. The choice should match the paths and policy of your target systems. The -u option makes an unset variable an error; the ${1-} form supplies a default so the example still works without an argument.

2. Add strictness intentionally

A common starting point is:

set -euo pipefail
  • -e (errexit) exits in many, but not all, contexts when a command fails.
  • -u (nounset) reports an unset variable.
  • pipefail makes a pipeline fail when a component other than the last one fails.

These options improve error detection but do not replace deliberate checks. Commands used as conditions in if, while, until, and parts of &&/|| lists have special -e behavior. Test failure paths rather than assuming one setting handles every case.

3. Make it executable and invoke it

chmod 750 backup.sh
./backup.sh /srv/app

Executing with ./backup.sh uses the shebang. Running bash backup.sh explicitly also selects Bash, even if the executable bit is absent; running sh backup.sh may select a different shell and can break Bash-specific syntax.

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

Control flow for administration tasks

Conditions with if and tests

path=/srv/app
if [[ -d $path ]]; then
    printf '%s existsn' "$path"
elif [[ -e $path ]]; then
    printf '%s exists but is not a directoryn' "$path"
else
    printf '%s is missingn' "$path"
fi

[[ ... ]] is Bash’s conditional command and avoids several word-splitting and pattern pitfalls associated with the older [ ... ] syntax. Common file tests include -e (exists), -f (regular file), -d (directory), and -r/-w/-x (readable, writable, executable by the current process).

Use case for selected modes

case ${1-} in
    start)  printf 'Startingn' ;;
    stop)   printf 'Stoppingn' ;;
    status) printf 'Checking statusn' ;;
    *)      printf 'Usage: %s {start|stop|status}n' "$0" >&2; exit 2 ;;
esac

case is easier to extend than a long chain of string comparisons and supports shell patterns for matching.

Loops and functions

for file in /var/log/*.log; do
    [[ -e $file ]] || continue
    printf 'Log: %sn' "$file"
done

report_size() {
    local target=$1
    du -sh -- "$target"
}

report_size /var/log

The guard inside the loop handles the case where an unmatched glob remains literal rather than naming a real file. Functions group reusable operations. Declare function-local variables with local so they do not unexpectedly overwrite variables in the calling scope. Pass values as arguments and return a status with return; a function’s printed text and its success/failure status are separate channels.

Bash and sh: related, but not interchangeable

Bash was designed to be largely compatible with the Bourne shell and to support POSIX shell conventions, but Bash adds features. The name sh can refer to different implementations depending on the distribution, and /bin/sh is not guaranteed to be Bash. It may be a smaller POSIX shell selected for startup speed, footprint, or policy.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Comparison axis Bash POSIX sh target
Portability Available on most Linux systems and many Unix-like systems, but not guaranteed everywhere. Targets the common POSIX shell language and is often the safer choice for minimal environments.
Interactive tools Command-line editing, history, aliases, job control, programmable completion, and other interactive facilities. Interactive features vary by implementation and are generally less extensive.
Programming features Arrays, [[ ... ]], arithmetic syntax, selectable options, and other Bash extensions. Use only POSIX-defined syntax; Bash arrays and [[ ... ]] are not portable assumptions.
Startup behavior Uses Bash-specific startup files according to whether it is a login, interactive, or non-interactive shell. Startup files and behavior depend on the implementation invoked as sh.
Distribution mapping May be the user’s login shell or an explicitly selected interpreter. /bin/sh mapping and the default login shell vary by distribution; inspect them on the target server.

Choose deliberately. If a script uses arrays, [[ ... ]], mapfile, or other Bash-only features, use a Bash shebang and run it with Bash. If the script must run in a minimal POSIX environment, write and test it as POSIX sh instead of relying on Bash accidentally being available.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Interactive Bash versus non-interactive Bash

An interactive shell reads commands from a terminal and commonly enables line editing, history, aliases, job control, and completion. A non-interactive shell reads a script or command string. It may not read the same startup files, and aliases or functions available in your terminal may not exist in automation.

For predictable server jobs:

  • Use an explicit shebang and invoke the intended interpreter.
  • Use absolute paths for critical programs when the execution environment has a restricted PATH.
  • Do not rely on interactive aliases, prompts, terminal dimensions, or human confirmation.
  • Set required environment variables in the script or job configuration, and log important decisions.

Login and interactive startup configuration can also alter behavior. A command that works at a prompt may fail under a scheduler because the scheduler starts a non-interactive shell with a different working directory, environment, and PATH.

Common Bash mistakes and how to diagnose them

“Command not found”

Check spelling, the executable’s presence, and the current PATH:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
printf '%sn' "$PATH"
command -v program
ls -l /path/to/program

A script launched by a scheduler may have a shorter PATH than your login shell. Use the absolute path or define the required path explicitly.

“Permission denied”

The file may lack the execute bit, the directory may not be searchable, or a security policy or mount option may block execution. Inspect permissions with ls -l, add the appropriate execute permission with chmod, or invoke the script through Bash when direct execution is not required.

Unexpected splitting or globbing

Print the argument count and each argument separately to see what Bash actually passed:

printf 'argc=%sn' "$#"
printf '<%s>n' "$@"

Quote expansions, use arrays for collections of words, and use -- before a variable that may begin with a hyphen when the called program supports that convention:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
files=(/srv/app/*.conf)
for file in "${files[@]}"; do
    cp -- "$file" /srv/app/archive/
done

“Bad substitution” or syntax errors

The script may be running under sh even though it contains Bash syntax. Check the shebang, execute it as ./script or bash script, and avoid invoking it as sh script unless it is intentionally POSIX shell code.

A pipeline hides a failure

Without pipefail, the pipeline’s status is normally the status of its last command. Enable set -o pipefail when an earlier failure must make the pipeline fail, and test the resulting behavior with both successful and failing inputs.

A practical learning path

  1. Master navigation and files: practise pwd, cd, ls, paths, permissions, and safe use of cp, mv, and rm.
  2. Learn composition: use grep, sort, uniq, head, tail, pipelines, and redirections while checking exit statuses.
  3. Practise data handling: write quoted variables, positional arguments, loops, functions, and case statements.
  4. Make scripts operational: add a Bash shebang, clear usage output, input validation, logging, explicit paths, and a deliberate error policy.
  5. Test failure paths: try missing files, empty input, spaces in names, permission errors, unavailable commands, and interrupted operations.
  6. Use a reference: consult the GNU Bash Reference Manual (Edition 5.3, updated 18 May 2025) for exact grammar, expansion order, built-ins, options, startup rules, and portability details.

The most valuable Bash habit is to reason about what each expansion, redirection, and exit status does before a command reaches a production server. Once that model is clear, interactive administration and repeatable automation become variations of the same language.

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.

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