Shell Scripting Basics

Turn command sequences you type again and again into Bash scripts. Start each script with a shebang line and make it executable. Store values in variables, quote them safely and capture command output. Use exit codes to report success and failure, and test files, text and numbers to choose what happens next. Repeat work with for, while and until loops, and pass arguments so one script handles many cases. Finally, schedule your scripts to run automatically with cron or systemd timers, and check that each run succeeded.

BeginnerLesson 4 of 7 in Linux Basics Course

What you will learn

  1. Turn a sequence of commands you type again and again into a script file, start it with the right interpreter line, and run it from any directory.
    • Turn repeated commands into a script file
      Write commands into a plain text file in order, add comments, and start the file with a shebang line that names Bash as the interpreter.
    • Run a script and choose where it lives
      Make a script executable, run it by path or through an interpreter, put it in a directory on the PATH, and know when a script can change your current shell.
  2. Keep text, numbers and command output in variables, read input from the user, and use quoting so that spaces and special characters reach commands unchanged.
    • Create, read and export variables
      Assign and use shell variables, choose good names, read input from the user, and pass variables to the programs a script starts.
    • Quote words, variables and special characters
      Decide between single quotes, double quotes and backslashes, and quote every variable expansion so that spaces and wildcards in values cannot break a command.
    • Capture command output and calculate with integers
      Store a command's output with command substitution and do whole-number arithmetic, for example to build dated file names and keep counters.
  3. Read and set the exit status of commands and scripts, chain commands on success or failure, and find and stop errors before they cause damage.
    • Read, set and chain exit codes
      Use the exit status that every command returns, end a script with a meaningful code, and run commands only when an earlier one succeeded or failed.
    • Find errors and stop a script safely
      Check and trace a script to find mistakes, send error messages to standard error, and make a script stop on failures, unset variables and failed pipelines.
  4. Test files, text and numbers, choose what to do with if and case, and repeat commands over lists, files and lines with for, while and until loops.
    • Test files, strings and numbers
      Write conditions with test, [ ] and [[ ]] that check whether files exist, compare text and compare whole numbers, and combine or negate them.
    • Choose actions with if, elif, else and case
      Run different commands depending on a condition with if, elif and else, and match one value against several patterns with case.
    • Repeat commands over lists and files with for
      Loop over a list of words, the files that match a pattern, a range of numbers or a script's arguments, and handle file names with spaces.
    • Repeat until a condition changes with while and until
      Run commands while a condition succeeds or until it does, read a file line by line, and control loops with break, continue and sleep.
  5. Give a script its input on the command line, read the arguments inside the script, check them, and supply defaults so the same script works for many cases.
    • Read positional parameters and the argument count
      Use $0, $1 to $9, ${10}, $# and "$@" to read the words given after a script's name, and use shift to work through them.
    • Check arguments, show usage and set defaults
      Validate the number and kind of arguments, print a usage message and exit with an error code, use default values, and handle simple options.
  6. Run scripts at fixed times or intervals without being logged in, with cron or with systemd timers, and check that scheduled runs happen and succeed.
    • Schedule jobs with cron
      Write crontab entries with the five time fields, edit and list them with crontab, and prepare scripts for cron's minimal environment.
    • Schedule jobs with systemd timers
      Pair a service unit with a timer unit, write calendar and interval schedules, enable the timer, and check the next run and the logs.

Shell Scripting study guide

6 chapters

1

Write and run your first Bash scripts

A few commands become easier to repeat when they live in one script. You will give the file a Bash interpreter, keep its instructions readable, and choose how to start it. File permissions, command search paths and child shells explain several common surprises: why a script works with bash but fails with ./, why its name is not found, and why its directory changes do not move your terminal.

1.1 Turn repeated commands into a script file

A shell script saves a sequence of commands in a plain text file. Bash reads the commands in order, just as it reads commands you enter at a prompt. A script can repeat a task without someone typing each step, and it gives you one place to correct or improve that task.

Put the interpreter on the first line

The shebang tells Linux which program should read an executable script. Start a Bash script with #!/bin/bash. The #! must be the first two characters in the file: no spaces or blank line before them. Bash is the Bourne Again Shell. It is widely used as an interactive shell on Linux and also reads script files.

An interpreter is a program that reads and runs another language. The same mechanism lets #!/usr/bin/python3 select Python or #!/usr/bin/awk -f select awk. The rest of a Bash script must contain shell commands that Bash understands.

Create backup-notes.sh in a plain text editor such as nano or vi:

#!/bin/bash
# Keep a copy of the notes and show the backup folder.
mkdir -p ~/backup
cp ~/notes.txt ~/backup/
ls -l ~/backup

Bash first makes the destination directory, then copies the notes, then lists the destination. mkdir -p also succeeds when the directory already exists, so a later run can reuse it. The source file ~/notes.txt must exist for the copy to work. Saving the commands does not create that source file.

Make the file readable to people

The second line is a comment. Outside quoted text, # begins a comment at the start of a word; Bash ignores the rest of that line. Write comments about the purpose of a step, such as why a backup must be kept. Blank lines and indentation make groups of commands easier to see. Keep indentation consistent as you add conditions and loops.

The .sh ending helps a reader recognize a shell script. Linux does not require that ending, and adding it does not select Bash or grant execute permission. A file named backup-notes can run the same commands.

Match the shell to the language
First lineHow the interpreter is chosen
#!/bin/bashUses Bash at the fixed path /bin/bash.
#!/usr/bin/env bashUses env to find Bash through PATH.
#!/bin/shUses the system's sh, which may be a different shell.

On Debian and Ubuntu, /bin/sh normally selects dash. Bash features such as [[ ... ]] fail there. A Bash script needs a Bash interpreter even if simple commands happen to work under either shell. Calling a file something.sh does not fix a mismatched shebang.

Recognize a line-ending problem

Windows editors can save a carriage return before each newline. On Linux, that invisible character may become part of the shebang path. An error such as /bin/bash^M: bad interpreter: No such file or directory points to this problem, even though /bin/bash exists.

Convert the file to Unix line endings with dos2unix backup-notes.sh, then save future scripts with Unix line endings in the editor. The commands in the file can be correct while the extra character prevents Linux from starting their interpreter.

Key Takeaways

  • A script is plain text containing commands to run in order.
  • The shebang must begin at the very start of the file and name the right interpreter.
  • Use comments and indentation to make the purpose and structure clear.
  • A .sh extension is a convention; it does not select a shell.
  • Unix line endings prevent a hidden carriage return from breaking the shebang.

1.2 Run a script and choose where it lives

A script can be read by Bash directly or started as a program. These routes differ in the permissions they need and in how the interpreter is chosen. The path you type also determines whether the shell searches for a command or uses one exact file.

Two ways to start the same file
bash backup-notes.sh
chmod u+x backup-notes.sh
./backup-notes.sh

The first command starts Bash and asks it to read the file. The script needs read permission; its own execute bit is not required. Bash treats the shebang as a comment because the interpreter has already been chosen.

The second command adds execute permission for the file's owner. In a permission string such as -rwxr--r--, the owner's x means executable. The third command runs the file as a program and uses its shebang. chmod +x is also common; without a named permission class, the umask limits which classes gain execute permission. chmod u+x explicitly targets the owner.

If ./backup-notes.sh reports "Permission denied," check execute permission and access to the path. If bash backup-notes.sh works, that does not establish that direct execution has the permissions it needs.

Understand the command search

./ means the current directory. When a command contains a slash, Bash follows that path. When you type a name without a slash, Bash searches the directories in PATH. The current directory is normally absent from that list, so being inside a script's directory is not enough to run it by name alone.

echo "$PATH"
command -v backup-notes.sh
type backup-notes.sh

PATH is a colon-separated list, such as /home/sam/.local/bin:/usr/local/bin:/usr/bin:/bin. The first matching executable wins. The last two commands help identify what a name will run, including an older copy elsewhere on PATH.

To make a personal script available by name, put it in a directory on your PATH. Many distributions add ~/bin or ~/.local/bin at login when that directory exists; check your actual PATH. /usr/local/bin is a common location for scripts shared by users and needs administrator rights to write to.

Choose a child shell or the current shell

Running ./settings.sh or bash settings.sh starts a child Bash process. Its variables and directory changes belong to that child. A script containing cd /var/log does not move the terminal that launched it. When it ends, you are still in the terminal's original directory.

Sourcing reads commands into the current shell:

source ./settings.sh
# Equivalent short form:
. ./settings.sh

Use this for a settings file meant to change your session. Sourcing needs read permission, not execute permission, and a shebang does not launch a separate interpreter. Sourcing a Bash settings file therefore assumes that the current shell understands Bash syntax.

Read the result of a run

Immediately after a script ends, echo $? shows its exit status. Unless the script uses exit with a chosen code, its status comes from its last command. A successful final listing can therefore hide an earlier failed copy; later error checks will let the script report the task's actual result.

Key Takeaways

  • bash file reads a script with Bash; ./file needs execute permission and uses the shebang.
  • PATH is searched only for command names without a slash.
  • Check a command name with command -v or type.
  • Execution starts a child shell; sourcing can change the current shell.
2

Store values in variables and quote them safely

Variables let the same commands work with changing paths, names and counts. Quoting keeps those values intact while Bash builds a command line, so a space in a name stays part of the name. You will read input, pass values to child programs, capture command output and calculate with integers. These pieces work together when a script builds a dated file name or keeps a running count.

2.1 Create, read and export variables

A variable gives a value a name so the same script can reuse it. Bash variables normally hold text, even when that text looks like a number. You assign the value once and expand the name wherever a command needs that value.

Assign without spaces
city=Lisbon
greeting="Good morning"
echo "$greeting from $city"

The output is "Good morning from Lisbon." There are no spaces around the assignment =. In city = Lisbon, Bash treats city as a command and the other words as arguments. Quotes around "Good morning" keep both words in one value.

Use letters, digits and underscores in variable names, with a letter or underscore first. Names are case-sensitive: file, File and FILE are different. Choose lowercase names such as backup_dir for values owned by your script. This helps avoid replacing environment variables such as PATH or HOME. Setting PATH=/data changes command lookup and can make ordinary commands stop being found.

Mark where a name ends

A dollar sign introduces an expansion. Braces show the boundary when more text immediately follows:

file=report
echo "${file}_old"

This prints "report_old." With "$file_old", Bash looks up a variable named file_old, not file followed by _old. Without set -u, an unset name expands to empty text. A misspelling can therefore produce an empty path or message with no immediate error.

A value such as count=5 stores the text "5." Adding text beside an expansion does not calculate: echo "$count+1" prints "5+1." Arithmetic expansion gives numbers a different role when calculations are needed.

Read a line of input
read -r -p "Your name: " name
echo "Hello, $name"

read stores the entered line in name. -p supplies the prompt and -r preserves backslashes. With the input "Lee Kim," the greeting is "Hello, Lee Kim." A script run without a person present needs its input from arguments, files or defaults; a prompt cannot supply a missing value reliably.

Export values for child programs

An ordinary shell variable belongs to the shell where it is set. Exporting marks it for inclusion in the environment copied to programs that shell starts:

export BACKUP_DIR=/mnt/usb
bash backup-notes.sh

The child Bash can now read BACKUP_DIR. If that child assigns a different value, the parent keeps its own value. Environment inheritance moves from parent to child; it does not send later edits back up.

NameUsual meaning
HOMEHome directory
USERUser name
PWDCurrent working directory
PATHDirectories searched for command names

env or printenv displays environment variables. unset name removes the variable named name; it does not assign an empty string. Under normal expansion both may look empty, but an unset-variable check can distinguish a missing variable from a set one.

Key Takeaways

  • Assignments use name=value with no spaces around =.
  • Variable names are case-sensitive; use lowercase names for your own values.
  • ${name} marks the end of a variable name before adjoining text.
  • read -r reads input while preserving backslashes.
  • Exported values are copied to child processes, never back to a parent.
  • unset removes a variable.

2.2 Quote words, variables and special characters

Bash reads a command before the program receives its arguments. Spaces separate words, and characters such as *, ?, $, ;, &, |, < and > have special roles. Quoting controls which of those roles remain active.

Choose what should expand
FormWhat Bash does
'fixed text'Keeps everything inside literal.
"text with $name"Preserves spaces and wildcard characters but expands variables, command output and arithmetic.
\*Treats the next character literally outside quotes.

With HOME=/home/sam, compare:

echo 'Home: $HOME *'
echo "Home: \$HOME *"

The first prints "Home: $HOME *". The second prints "Home: /home/sam *". The asterisk stays literal in both, but only double quotes allow the variable to expand. A single quote cannot be placed directly inside a single-quoted string.

Inside double quotes, backslashes can protect a dollar sign, backtick, double quote or backslash. For example, echo "Price: \$5" prints "Price: $5". Outside quotes, echo 5 \* 3 prints "5 * 3", and touch my\ file.txt creates one file with a space in its name.

Follow an unquoted value through Bash
file="my report.txt"
cp $file backup/

First Bash expands the variable. Then word splitting divides its value at spaces, tabs and newlines. The command receives my, report.txt and backup/ as separate arguments. It tries to copy two source files instead of the one intended file.

If a resulting word contains wildcard characters, globbing then replaces it with matching file names. A stored value of *.txt can become several file arguments when expanded without quotes. These extra steps happen before cp starts; the program cannot recover your intended grouping.

cp "$file" backup/

Now the expanded value stays one argument, "my report.txt". The quote characters themselves are removed by Bash; they are instructions for parsing, not part of the path passed to cp. Put double quotes around variable and command substitutions whenever their full value should stay together.

Keep an empty value present

Quoting also matters when a value is empty. In [ $answer = yes ], an empty answer disappears, leaving an invalid test. [ "$answer" = yes ] passes an empty string as one argument, so the test is valid and false.

The same grouping rule applies to literal words. mkdir new folder creates two directories, while mkdir "new folder" creates one. Quoting an entire path is useful whether its space was typed directly or came from a variable.

Combine literal and expanded text

Adjacent quoted parts with no separating space form one word. With price=8, echo 'Price: $'"$price" prints "Price: $8". The fixed part is single-quoted and the changing part is double-quoted. A search pattern can likewise be kept fixed: grep 'cost: \$[0-9]' prices.txt passes its pattern without shell expansion, leaving grep to interpret the pattern.

Key Takeaways

  • Single quotes keep text literal; double quotes allow expansions while keeping values together.
  • Unquoted expansions undergo word splitting and then filename expansion.
  • Quotes preserve an empty value as an argument.
  • A backslash can make one special character literal.
  • Adjacent quoted parts form one word when no space separates them.

2.3 Capture command output and calculate with integers

Commands often produce a value that another step needs. Bash can capture that output, place it inside text, or use it in a calculation. Command substitution and arithmetic expansion have similar punctuation but do different jobs.

Capture standard output
today=$(date +%F)
cp notes.txt "notes-${today}.txt"

$(...) runs a command and replaces the substitution with its standard output. On 2026-10-22, today holds "2026-10-22" and the copy is named notes-2026-10-22.txt. A year-month-day date keeps file names in date order when sorted by name. You can also put the substitution directly in the name: "notes-$(date +%F).txt".

Trailing newline characters are removed from captured output. The older backtick form also captures output, but $(...) is easier to read and nest. Quote a substitution used as a command argument, such as "$(date +%F)", to keep its result together.

Command substitution captures standard output only. Errors sent to standard error still go to their usual destination. result=$(some_command 2>&1) combines both streams inside the capture when that is what the script needs. Mixing errors into a value intended to contain a number would make later arithmetic fail.

Capture a count without a file name
lines=$(wc -l < access.log)
echo "Lines: $lines"

If the file contains 342 newline characters, wc supplies the count 342. Redirecting the file into wc makes its output contain only the count; wc -l access.log also prints the file name. A numeric-looking result can then be reused, provided the command succeeded.

Calculate with whole numbers

$(( ... )) performs arithmetic expansion:

echo "$(( 17 / 5 ))"
echo "$(( 17 % 5 ))"

The results are 3 and 2. Integer division drops the remainder; % returns it. Addition, subtraction and multiplication use +, - and *. Variable names can appear without a dollar sign inside the expression: total=$(( price * qty )).

With a=5, echo "$a+3" prints "5+3" as text. echo "$(( a + 3 ))" calculates and prints 8. The extra parentheses select arithmetic; $(a + 3) would instead try to run a command named a.

A counter begins with a value such as count=0 and updates with count=$(( count + 1 )). Bash also offers (( count++ )); this is an arithmetic command with its own exit status, which matters in scripts using error options.

Keep the needed precision

Bash arithmetic uses integers. 7 / 2 gives 3, and a decimal value such as 2.5 causes an arithmetic syntax error. A separate tool can calculate decimals:

echo "scale=2; 7 / 2" | bc

This produces 3.50. With integer arithmetic, multiply before dividing when the product retains a fraction you still need. For 45 out of 60, $(( 45 * 100 / 60 )) gives 75. Dividing first gives zero for 45 / 60, so $(( 45 / 60 * 100 )) remains zero; the lost fraction cannot be recovered by later multiplication.

Key Takeaways

  • $(command) captures standard output and removes trailing newlines.
  • $((expression)) calculates with integers.
  • Use / for integer division and % for its remainder.
  • A counter stores its updated value after each increment.
  • Multiplying before dividing can prevent early loss of integer precision.
  • Use a tool such as bc when a decimal result is required.
3

Report success and failure with exit codes

A script needs a result that both people and programs can understand. Printed output explains what happened, while an exit status lets another command decide whether to continue. You will save that status, check dependent steps and keep pipeline failures visible. Syntax checks, execution traces and error options help find the cause of a failed run before later commands make its effects harder to see.

3.1 Read, set and chain exit codes

A command reports its result in two ways: it may print text, and it returns an exit status. Programs use that status to decide what happens next. A script can print a reassuring message and still have failed, or print nothing and succeed.

Read a status before it changes

An exit status is a whole number from 0 to 255. Zero means success. A nonzero value reports failure or another documented result. grep, for example, uses 0 for a match, 1 for no match and 2 for an error. A search finding no match can be a normal outcome for your task even though its status is nonzero.

$? holds the status of the most recent command:

grep -q root /etc/passwd
status=$?
echo "Search status: $status"

The assignment saves the result before another command replaces it. In ls /missing; echo $?; echo $?, GNU ls returns 2 for the inaccessible command-line path. The first echo prints 2, then succeeds. The second prints 0, the status of that echo, not the original listing.

End with a useful result

exit 1 ends a script immediately and reports 1 to its caller. exit 0 reports success. An exit without a number uses the previous command's status; reaching the end normally uses the last command's status. Choose codes within 0–255: exit 256 wraps to 0 and would incorrectly report success.

Some statuses come from Bash itself:

CodeMeaning
127The command was not found.
126The command was found but could not be executed.
128 + nThe command ended because of signal number n.

Ctrl+C sends signal 2, so a command terminated that way commonly reports 130. Ordinary programs also document their own codes; 1 and 2 do not have one universal meaning across all tools.

Chain dependent commands
mkdir -p ~/backup && cp notes.txt ~/backup/
cd /data/reports || exit 1

&& runs the next command only after success. Here the copy depends on creating or finding the backup directory. || runs the next command only after failure. Here a failed directory change stops the script before later commands use the wrong working directory.

true returns 0; false returns 1. A leading ! reverses a command's success or failure. ! grep -q ERROR app.log succeeds when the search finds no matching line, although it also succeeds if grep fails with an error; use explicit status checks when those outcomes must be distinguished.

Check a whole pipeline
grep ERROR missing.log | wc -l

By default, a pipeline has the status of its final command. grep fails on the missing file, but wc reads an empty stream, prints 0 and succeeds. The pipeline's status is therefore 0.

set -o pipefail makes a pipeline fail when any member fails. It reports the rightmost nonzero status, or 0 if all commands succeed. This makes upstream errors visible to if, &&, || and the caller. A scheduler can then distinguish a successful report from one that never read its input.

Key Takeaways

  • Exit status 0 means success; nonzero results have meanings documented by each program.
  • Save $? immediately if you need to use it after another command.
  • exit supplies a result to the script’s caller.
  • && continues after success, and || continues after failure.
  • Without pipefail, a pipeline reports only its last command’s status.

3.2 Find errors and stop a script safely

Bash normally continues after a command fails. If the next command depends on that work, continuing can turn one small failure into a larger problem. Check dependent steps explicitly, then use syntax checking and tracing to find why a run behaves differently from your plan.

Stop before a failed step spreads
cd /backups || exit 1

Only commands after a successful directory change can now run. This matters before file operations: if a directory change fails and the script continues, relative paths still refer to the old directory. Check the destination before running commands that change or delete files.

An error message and a failure status serve different readers:

echo "Error: $file not found" >&2
exit 1

The message goes to standard error, leaving standard output available for data. With ./report.sh > report.txt, the message remains visible on the terminal while normal output enters the file. The nonzero status lets another program detect the error without reading the sentence.

Check syntax, then trace execution
CommandWhat it does
bash -n script.shReads syntax without executing commands.
bash -x script.shRuns the script and prints expanded commands before executing them.

A missing fi, done or closing quote is a syntax problem. bash -n can locate it, often through an "unexpected end of file" message. A syntactically valid but misspelled file path needs an execution check.

Tracing prefixes commands with +. A line such as cp "$src" "$dest" reveals the actual paths it received. set -x and set +x turn tracing on and off around one section.

Understand the error options

set -euo pipefail combines three controls:

  • -e exits after an unhandled command failure.
  • -u reports an unset variable as an error.
  • pipefail makes failed pipeline members affect the pipeline status.

-e leaves room for expected failures in if, while and until conditions, negated commands, and nonfinal commands in && or || lists. Without pipefail, a failure before the final pipeline command does not trigger an exit. Explicit checks remain useful because they state what the script should do when a particular step fails.

An arithmetic command also reports true or false. (( count++ )) returns status 1 when the old value is 0, so an unhandled use under -e can stop the script even though the increment happened. The assignment count=$(( count + 1 )) avoids that arithmetic-command status.

-u catches a typo such as $filname when only filename was set. It does not reject a variable that exists but is empty. ${dir:?not set} checks for missing or empty dir at that expansion. An expected no-match result can be handled with a test; command || true suppresses every failure of that command, so use it only when all those failures are acceptable.

Read messages and clean up

"Command not found" points to a name, installed program or PATH problem. "Permission denied" points to execution or file access. "Unary operator expected" commonly comes from an empty unquoted value in [ ]; "integer expression expected" means a numeric test received text.

An EXIT trap installs cleanup, such as trap 'rm -f "$tmpfile"' EXIT, after tmpfile has been assigned. The quoted command runs when Bash exits, including an explicit exit or an error exit under -e. ShellCheck can also inspect scripts for common quoting and logic mistakes before a run.

Key Takeaways

  • Check a step before running commands that depend on it.
  • Write diagnostics to standard error and report failure with a nonzero status.
  • bash -n checks syntax; bash -x executes and traces.
  • set -e has exceptions for tested failures.
  • set -u catches unset variables, while ${name:?message} also rejects empty values.
  • An EXIT trap can remove a temporary file when the script ends.
4

Make decisions and repeat work

A useful script chooses actions and repeats them over changing input. Tests turn questions about files, text and numbers into success or failure. Branches select an action, while loops apply commands to lists or keep going until a condition changes. You will trace the value used on each pass and preserve spaces, records and counters as data moves through the script.

4.1 Test files, strings and numbers

A condition in Bash is a command whose status means true or false. Tests let you ask about paths, text and numbers. Choose an operator for the kind of value you have; the same printed characters can have different meanings as text and as a number.

Give the test its own words

test -f notes.txt and [ -f notes.txt ] ask whether a path refers to an existing regular file. [ is a command, and its final argument must be ]. Spaces separate its arguments:

[ -f "$file" ]

Without the space after [, Bash may look for a command named [-f. In [ "$a"="$b" ], the missing spaces around = form one nonempty word, which tests true even if the values differ. Operators must be separate arguments.

Choose a file test
OperatorTrue when the path…
-eExists.
-fIs an existing regular file.
-dIs an existing directory.
-r, -w, -xIs readable, writable or executable by the current user.
-sExists and has a size greater than zero.
-LIs a symbolic link.

For a directory named logs, -e and -d succeed while -f fails. File tests normally follow a symbolic link to its target; -L checks the link itself. A broken link can therefore satisfy -L and fail -e.

Compare text or whole numbers

String tests use = and != for equality and inequality. -z checks for zero length and -n for nonzero length. [ "$answer" = yes ] matches exactly "yes"; "Yes" and "yes " are different text. Double quotes keep an empty value in the test.

Integer comparisons use -eq, -ne, -lt, -le, -gt and -ge: equal, not equal, less than, less than or equal, greater than, and greater than or equal. [ "$count" -gt 100 ] asks whether the number exceeds 100.

[ 07 -eq 7 ]
[ 07 = 7 ]

The numeric comparison succeeds because the values are equal. The string comparison fails because one text begins with an extra zero.

Do not use a bare > for a numeric test inside [ ]. In [ 10 > 9 ], Bash redirects output to a file named 9 and tests the remaining nonempty word. In Bash [[ 10 < 9 ]], the operator compares strings, so the initial character 1 sorts before 9. Use -lt or -gt for numeric comparisons, or Bash arithmetic such as (( count > 100 )).

Combine conditions

[ ! -d "$dir" ] succeeds when the path is not an existing directory; it can be a regular file or be missing. [ -f "$f" ] && [ -r "$f" ] succeeds only when both tests succeed. Separate tests joined by || succeed when at least one succeeds.

Bash also has the conditional keyword [[ ... ]]. It prevents word splitting of its variable expansions and allows && and || inside. An unquoted pattern on the right of == performs pattern matching: [[ $file == *.log ]] matches "app.log". Quoting that pattern would make it literal. This syntax needs Bash; dash does not implement it.

Key Takeaways

  • A test reports true with status 0 and false with status 1.
  • Put spaces around [ and its operators, and close with ].
  • File tests distinguish existence, type and access.
  • String equality compares text; numeric operators compare integer values.
  • Use -gt and -lt for numbers, not bare redirection symbols.
  • Negation reverses a test; && requires both conditions.
  • Bash [[ ... ]] supports patterns and avoids word splitting.

4.2 Choose actions with if, elif, else and case

Conditions become useful when they select actions. An if block chooses a branch from command results. A case block selects a branch by matching one value against patterns. Both make the available actions visible in the script.

Let a command choose a branch
if grep -q ERROR app.log; then
  echo "Errors found"
else
  echo "No successful match"
fi

if runs the command and enters the first branch when it returns 0. Otherwise it enters else. The search itself is the condition; there is no need to run it, print something, and then inspect $?. A grep error is also nonzero, so this example describes the else branch as no successful match, not proof that the file contains no errors.

then must begin a new command: put it on the next line or after a semicolon. fi closes the block. A missing fi often produces an end-of-file syntax error because Bash is still waiting for the block to close.

Order overlapping conditions

elif adds a test that runs only when earlier tests failed. For a whole-number disk-use percentage:

if [ "$usage" -ge 90 ]; then
  echo "CRITICAL: disk ${usage}% full"
elif [ "$usage" -ge 75 ]; then
  echo "WARNING: disk ${usage}% full"
else
  echo "OK: disk ${usage}% full"
fi

With usage=82, the first test fails and the second succeeds, producing "WARNING: disk 82% full". With 95, the first branch runs and the warning branch is skipped. If the 75 test came first, 95 would match it and never reach the critical test. Arrange overlapping ranges so the more restrictive condition gets its turn.

Only the first successful branch runs. else needs no condition and handles what remains. Either elif or else can be omitted when the task does not need it.

Match one value with case
case "$1" in
  start) echo "Starting" ;;
  stop) echo "Stopping" ;;
  y|Y|yes) echo "Confirmed" ;;
  *) echo "Usage: $0 start|stop|y|Y|yes" >&2; exit 2 ;;
esac

Each pattern ends with ), each branch ends with ;;, and esac closes the block. The | within a pattern gives alternatives. The final * is the catch-all and belongs last because it matches every value.

case uses file-style patterns, not regular expressions. *.jpg matches a value ending in .jpg. [0-9]* matches a value whose first character is a digit; the * can then match any following text, so it also matches "7draft". It does not prove that an entire value contains digits only.

Choose the structure that fits

Use case when one input such as a subcommand or answer has several named forms. Use if when conditions examine different things, such as whether a file exists and whether a count exceeds a limit. A pattern match asks about the characters in a value; a file test asks about the path on disk. A name ending in .log can match a pattern even when no such file exists.

Key Takeaways

  • if branches on a command’s exit status; any command can be a condition.
  • elif tests run only after earlier conditions fail.
  • Order overlapping thresholds from the stricter one to the broader one.
  • case selects the first matching pattern and uses ;; to end each branch.
  • A pattern match does not establish that a file exists.

4.3 Repeat commands over lists and files with for

A for loop gives the same body of commands a new value on each pass. The list can contain literal words, matching file names, a number range or the script's arguments. Bash assigns one list item to the loop variable before running the body.

Walk through a fixed list
for host in web1 web2 db1; do
  echo "Checking $host"
done

The first pass has host=web1, the second host=web2, and the third host=db1. The loop prints three lines. do opens the body and done closes it. The variable remains available after the loop, holding its last assigned value.

A loop can also accumulate a result:

total=0
for n in 4 8 15; do
  total=$(( total + n ))
done
echo "$total"

The stored total changes from 0 to 4, then 12, then 27. Initialize the total before the loop so the first addition has a defined starting value.

Loop over matching files
for f in *.log; do
  [ -f "$f" ] || continue
  gzip -- "$f"
done

Bash expands the glob into matching names before the loop body runs. Each matched file name remains one list item even if it contains spaces. Quoting "$f" inside the body keeps that item together when it is passed to gzip. Here the regular-file test also skips directories named with a .log ending.

With Bash's default glob settings, an unmatched *.log stays literal. A loop without a guard would run once on the characters *.log and the command would fail. [ -e "$f" ] || continue skips nonexistent paths; use -f when only regular files are wanted.

Do not build a filename loop from $(ls). Its output is text, so word splitting can turn "my report.txt" into two list items. A glob already produces the file names with their boundaries intact.

Build a new name from each old one
for f in *.jpeg; do
  [ -f "$f" ] || continue
  mv -- "$f" "${f%.jpeg}.jpg"
done

${f%.jpeg} removes the ending .jpeg from the value. For "beach day.jpeg", the destination becomes "beach day.jpg". The two quoted arguments keep both paths intact. -- tells mv that subsequent arguments are names, even if one begins with -. Use a destination folder with no conflicting names when trying the example, since mv can replace an existing destination.

Count through a range

for i in {1..5} supplies 1 through 5. Bash expands the braces before expanding variables, so {1..$n} does not produce a variable-length range. For a stored limit, use the arithmetic form:

for (( i = 1; i <= n; i++ )); do
  echo "$i"
done

The first expression initializes the counter, the middle tests it before each pass, and the last updates it. for i in $(seq 1 "$n") also produces a number list for a suitable positive integer limit. The intentional unquoted substitution here supplies separate numbers.

For arguments, for arg in "$@" supplies one item per original argument. The shorter for arg; do means the same thing, including preservation of spaces inside each argument.

Key Takeaways

  • A for loop runs its body once per list item.
  • Globs provide whole file names; quote the loop variable in the body.
  • An unmatched glob stays literal under Bash’s default settings, so guard file loops.
  • Avoid filename loops built from $(ls).
  • Brace ranges are fixed before variable expansion; use an arithmetic loop for a variable limit.
  • "$@" preserves the original argument boundaries.

4.4 Repeat until a condition changes with while and until

Some repetition ends when a condition changes, not when a list runs out. while and until test before every pass. A while body runs while its condition succeeds; an until body runs while its condition fails. Either loop can run zero times.

Update what the condition depends on
i=1
while [ "$i" -le 3 ]; do
  echo "Attempt $i"
  i=$(( i + 1 ))
done

The condition first sees 1, then 2, then 3. The body prints those three attempt numbers and increases the counter each time. After the update to 4, the next test fails and the loop ends. If the update were missing, i would stay at 1 and the loop would keep running. Ctrl+C stops a running foreground loop.

To wait for a file:

until [ -f /tmp/export.done ]; do
  sleep 5
done

If the file already exists, the loop body never runs. Otherwise sleep 5 pauses five seconds before the next check. A pause prevents the loop from testing continuously and wasting processor time.

Read records as lines
while IFS= read -r line; do
  echo "Host: $line"
done < hosts.txt

read supplies one line on each pass and returns a failing status at end-of-file. IFS= preserves spaces at the start and end of the line; -r keeps backslashes. The input redirection after done feeds the whole loop, not just its final command.

A line such as "web1 production" stays one value. for line in $(cat hosts.txt) would split at spaces, tabs and newlines, treating those two words as separate iterations. That form cannot preserve arbitrary line-based records.

Keep changes after the loop

A pipe into a loop, such as cat hosts.txt | while read -r line; do ...; done, normally runs the loop in a child process in Bash. A counter changed there is lost to the parent when the child ends. Redirect the file into the loop when later commands need its accumulated values:

count=0
while IFS= read -r line; do
  count=$(( count + 1 ))
done < hosts.txt
echo "$count"

This processes newline-terminated lines and keeps the updated count in the current shell. No pipeline creates a child for the loop.

Handle the final record and early exits

A file can end with text but no final newline. read stores that last text yet reports failure, so a plain while read does not enter its body for that record. Add a second test to accept the stored nonempty text:

while IFS= read -r line || [ -n "$line" ]; do
  echo "Host: $line"
done < hosts.txt

break exits the loop immediately. continue skips the remaining commands in the current pass, then returns to the next condition check. A while true loop therefore needs a break, an exit or a signal to end; it can support a menu that breaks when the input is "q". Place an update before any continue that would otherwise skip it in a counter loop, or that pass could repeat forever.

Key Takeaways

  • while repeats after success; until repeats after failure.
  • Both loops test before entering their bodies.
  • Update a counter or pause a polling loop so it makes progress without busy waiting.
  • IFS= read -r preserves spaces and backslashes in line-based input.
  • Redirect a file into the loop to keep changed variables in the current shell.
  • An extra nonempty-string test preserves a final record without a newline.
  • break leaves a loop; continue skips to its next pass.
5

Pass arguments to scripts

Command-line arguments let one script serve many tasks without editing its code. You will read individual values, preserve a complete argument list and consume leading options. Before work begins, count and validate the inputs, explain a correct call and supply defaults for optional values. The result can run at a prompt or unattended, with clear messages and an exit status when input is unusable.

5.1 Read positional parameters and the argument count

Arguments turn a script with fixed input into a reusable command. Bash places the words after the script name into positional parameters. Their boundaries are set by the calling shell, so quotes matter both when the script is called and when it uses those values.

Identify the parameters
./backup.sh /home/sam/docs /mnt/usb

Inside the script, $0 is ./backup.sh, $1 is /home/sam/docs, and $2 is /mnt/usb. $# is 2, the number of arguments. The script can use cp -r "$1" "$2" to copy the source folder to the destination without hard-coding either path.

$0 describes the name used to start the script; it is not counted in $#. With ./greet.sh Ana "Lee Kim", the first argument is "Ana" and the second is "Lee Kim". Without the quotes around the second name, the script would receive three arguments.

Use braces after the ninth argument

$1 through $9 name the first nine positional parameters. The tenth needs ${10}, and later positions follow the same form. $10 means the expansion of $1 followed by the literal character 0. If the first argument is "Ana", $10 becomes "Ana0" even when a tenth argument exists.

An omitted parameter expands to empty text under normal settings. With two arguments, $3 is missing; under set -u, expanding it causes an unset-variable error. Check the count before reading required positions, or use an expansion that supplies a default.

Preserve all the argument boundaries

Suppose a script receives "a b" and c. Compare:

ExpansionWords passed onward
"$@"Two: "a b" and "c".
"$*"One: "a b c", with the usual space separator.
$@Three: "a", "b" and "c" under normal splitting.

"$@" is the usual choice for forwarding arguments:

for arg in "$@"; do
  echo "Argument: $arg"
done

The loop runs twice for that call, once per original argument. With no arguments it runs zero times. Double-quoting "$@" has a special meaning: each positional parameter stays a separate word, so the quotes do not join the entire list into one word.

"$*" joins values using the first character of IFS, which is normally a space. That can be useful for a single display string, but it loses the individual boundaries a later command may need.

Consume parameters with shift

shift removes the first positional parameter, moves the later ones down and decreases $#. In the backup call, one shift makes $1 become /mnt/usb and the count become 1. shift 2 removes the first two parameters at once.

This supports a loop that examines $1, handles it, then shifts before the next pass. Test that arguments remain with [ "$#" -gt 0 ]; otherwise the script can read a missing value or try to shift beyond the available arguments. Shifting does not change $0, and it cannot change the argument list of the parent shell that launched the script.

Key Takeaways

  • $0 is the invocation name, while $1 onward hold arguments.
  • $# counts arguments, with quoted spaces kept inside one argument.
  • Use ${10} for the tenth argument; $10 is $1 followed by 0.
  • "$@" preserves arguments separately, while "$*" joins them.
  • shift removes leading parameters and changes the remaining positions.

5.2 Check arguments, show usage and set defaults

A script should establish that its input is usable before it starts work. Check the number of arguments, then check their meaning. Optional inputs can have defaults; missing required inputs need a clear diagnostic and a nonzero exit status.

Explain the expected call
if [ "$#" -ne 2 ]; then
  echo "Usage: $0 SOURCE DEST" >&2
  exit 2
fi

This checks the count before expanding $1 or $2, so it also works under set -u. The usage message goes to standard error. Capitals name values the user supplies, [DEST] would mean an optional destination, and FILE... means one or more file arguments. A help option such as -h normally prints usage and exits with 0 because asking for help is successful use.

An argument count alone cannot tell whether a source is usable. [ -d "$1" ] checks for an existing directory. A regular-file check can select files, but the operation itself still needs a result check: permissions or other errors can prevent a read.

Use a default or require a value
logdir=${1:-/var/log}
src=${1:?Usage: backup.sh SOURCE [DEST]}

The first form uses /var/log when the first parameter is missing or empty. The second stops a noninteractive script in either case and prints its message on standard error. The colon is part of both forms. A default is a value used for this expansion; ${1:-...} does not add an argument to the positional parameter list.

Keep a record of partial failure

A file-counting script can continue after a bad input while still reporting that the whole task was incomplete:

#!/bin/bash
if [ "$#" -eq 0 ]; then
  echo "Usage: $0 FILE..." >&2
  exit 2
fi
status=0
for f in "$@"; do
  if [ -f "$f" ]; then
    if lines=$(wc -l < "$f"); then
      echo "$f: $lines lines"
    else
      echo "$f: could not count lines" >&2
      status=1
    fi
  else
    echo "$f: not a regular file" >&2
    status=1
  fi
done
exit "$status"

For notes.txt missing.txt, the script prints the readable file's count and reports the missing file, then exits with 1. The assignment is tested directly, so a read failure does not become a successful empty count. Later success never resets the stored failure flag to 0.

Handle simple options before file names
verbose=0
while [ "$#" -gt 0 ]; do
  case "$1" in
    -v) verbose=1; shift ;;
    -h) echo "Usage: $0 [-v] FILE..."; exit 0 ;;
    --) shift; break ;;
    -*) echo "Unknown option: $1" >&2; exit 2 ;;
    *) break ;;
  esac
done

Each recognized flag is consumed with shift. The first ordinary argument ends option parsing; -- also ends it and is itself consumed. The remaining "$@" contains the file names, including a name beginning with - after --. Bash's getopts supports fuller short-option parsing.

An unattended script needs all required input from arguments, defaults or configuration. read can block waiting for a terminal or fail immediately at end-of-file. Replacing a missing argument with a prompt would leave a scheduled job without a dependable way to proceed.

Key Takeaways

  • Validate the count before expanding required arguments.
  • A usage message names required, optional and repeatable inputs.
  • ${1:-value} provides a default; ${1:?message} rejects a missing or empty value.
  • Check the operation’s result even after checking the input’s file type.
  • Preserve an overall failure status when continuing across several inputs.
  • Consume options with shift and honor -- as the end of options.
6

Schedule scripts to run automatically

An unattended script needs a schedule and a way to inspect its results. Cron expresses matching clock times in one line; systemd pairs a service with a timer. You will read both forms, supply the environment the job needs and keep its output available. Checking the next activation and the last task result separately helps distinguish a schedule that is active from work that actually succeeded.

6.1 Schedule jobs with cron

Cron starts commands at matching clock times. Each user's crontab stores that user's schedule, and jobs run as that user. A scheduled script needs both a correct time expression and an environment that provides its paths, inputs and output destination.

Read the five fields

Use crontab -e to edit your table and crontab -l to inspect it. crontab -r deletes the entire table without asking; use the editor to remove a single job.

Field, in orderValues
Minute0–59
Hour0–23
Day of month1–31
Month1–12
Day of week0–7; 0 and 7 mean Sunday
30 2 * * * /home/sam/bin/backup.sh

This runs every day at 02:30. Asterisks accept every value in a field. 0 9 * * 1-5 selects 09:00 on weekdays, and 0 0 1 * * selects midnight on the first day of each month. Times use the system's time zone unless the cron configuration specifies another supported zone.

Combine ranges, lists and steps

A comma lists values: 1,15 in the day-of-month field selects the first and fifteenth. A hyphen gives a range, and / introduces a step. */15 in the minute field selects 0, 15, 30 and 45.

*/20 8-10 * * * selects three minutes in each of three hours: nine daily runs, starting at 08:00 and ending at 10:40. Steps restart within their field. */45 runs at minute 0 and minute 45 of each hour; the gap to the following hour's minute 0 is only 15 minutes.

When both day-of-month and day-of-week are restricted, either match is enough. 0 12 13 * 5 selects noon on the 13th and on Fridays, not only Friday the 13th. The other fields still need to match.

Special strings replace all five fields: @hourly, @daily, @weekly, @monthly, @yearly and @reboot. @daily means midnight, while @reboot runs once at startup.

Supply paths and keep the output

Cron normally uses /bin/sh for its command line, a short PATH, and no interactive shell startup settings. An executable script's own Bash shebang still selects Bash. Use absolute paths for the script and its files, or set PATH at the top of the crontab.

30 2 * * * /home/sam/bin/backup.sh >> /home/sam/backup.log 2>&1

The job appends both output streams to the log. Without redirection, cron sends output by mail when mail is configured, including through MAILTO; otherwise output may be lost. A percent sign has a special role in a crontab command: it starts a newline and subsequent input. Escape a date format there, as in date +\%F. A date command inside a separate script uses ordinary %F.

Distinguish system schedules and catch-up jobs

/etc/crontab and files in /etc/cron.d/ add a user field after the five time fields. For example, 30 2 * * * root /usr/local/bin/backup.sh runs as root. Do not add that user field to a personal crontab.

Many distributions also run eligible executables in /etc/cron.daily/, /etc/cron.weekly/ and /etc/cron.monthly/. Debian's default run-parts naming rules skip names containing periods, so use backup-notes, not backup-notes.sh, there. Cron misses times that pass while the computer is off. Anacron can catch up periodic daily, weekly or monthly jobs after startup.

Key Takeaways

  • A personal crontab has five time fields followed directly by a command.
  • Lists, ranges and steps select values within each field.
  • Restricted day-of-month and day-of-week fields use an either-match rule.
  • Use absolute paths and an output log for unattended jobs.
  • Escape % in a crontab command, including date formats.
  • System crontabs add a user field; periodic folders also enforce eligibility rules.
  • Cron misses runs while the machine is off; anacron supports periodic catch-up.

6.2 Schedule jobs with systemd timers

Systemd separates the work from its schedule. A service unit describes the command to run. A timer unit describes when to start it. By default, backup.timer activates backup.service, using the shared base name to connect the pair.

Write the service and the calendar schedule

Place these system units in /etc/systemd/system/:

# backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

Type=oneshot suits a script that runs once and ends. Use an absolute command path and make the script executable with a Bash shebang. The timer supplies the schedule:

# backup.timer
[Unit]
Description=Run backup.service daily at 02:30

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true

[Install]
WantedBy=timers.target

OnCalendar matches dates and clock times. Here the wildcard date accepts every day. Persistent=true enables catch-up after the timer was inactive: if at least one calendar run was missed, it starts the service once on reactivation. It does not replay every missed run.

Load and activate the timer
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer

Reload after creating or changing unit files. enable --now starts the timer immediately and enables it for future boots. Enabling only the service does not create a schedule. Test the work separately with sudo systemctl start backup.service.

Choose clock time or elapsed time
SettingSchedule
OnCalendar=Mon..Fri *-*-* 09:00:00Weekdays at 09:00
OnCalendar=*:0/15Minutes 0, 15, 30 and 45 each hour
OnBootSec=10minTen minutes after boot
OnUnitActiveSec=1hOne hour after the service was last activated

Calendar expressions can use daily for midnight each day or weekly for Monday at midnight. Check an expression with systemd-analyze calendar "Mon..Fri 09:00" to see its interpretation and next occurrence.

The last two settings are monotonic timers: they measure elapsed time from an event. Combine them for a first run after boot and later hourly activations. Persistent=true applies to calendar timers; it does not add calendar catch-up to monotonic timers.

Inspect scheduling and results separately

systemctl list-timers shows the next and last activations. systemctl status backup.service shows the service's result, and journalctl -u backup.service shows its output and errors. A listed next run establishes that a schedule is active, not that the script's previous task succeeded.

Timers have a firing window. AccuracySec defaults to one minute; AccuracySec=1s narrows the coalescing window. RandomizedDelaySec deliberately adds a random delay to spread work across machines. These settings do not guarantee an exact start second under load.

Run a user's own timer

User unit files live in ~/.config/systemd/user/, and commands use systemctl --user, including daemon-reload, enable --now and list-timers. A user manager normally runs with that user's session. An administrator can enable lingering with loginctl enable-linger username to keep it available without a login.

Cron needs a compact time line and works without systemd. Timers add a service result and journal, calendar catch-up and more control. Neither normally loads ~/.bashrc; both need scripts that work with explicit paths and no interactive prompts.

Key Takeaways

  • The service says what to run; the timer says when.
  • Reload changed units and enable the .timer with --now.
  • Calendar timers follow clock time; monotonic timers measure from events.
  • Persistent=true catches up missed calendar work once on reactivation.
  • Check both the timer’s next activation and the service’s status and journal.
  • User timers need a running user manager, with lingering for runs without a session.

Shell Scripting examples

6 Walkthroughs

1

Save a repeatable folder report

9 steps
Build a small Bash report, run it by path and distinguish execution from sourcing.
  1. 1
    IntroductionGive a repeated task one home

    You often check where you are, list a folder and print a completion message. Saving the commands makes their order repeatable.

    A script is a plain text file read by an interpreter. You will create a small report, select Bash explicitly and run it in a child shell. The file’s name, execute permission and location each answer a different question about starting it.

  2. 2
    ExampleWrite the commands in order

    Save this as folder-report.sh in a plain text editor:

    #!/bin/bash
    pwd
    ls
    echo "Report finished"
    

    The shebang selects Bash when the file is executed directly. The comment explains the task but does not run. Bash then runs pwd, ls and echo in that order. The .sh ending helps a reader recognize the file; it does not select the interpreter.

  3. 3
    ChallengeCheck what selects the language

    A colleague changes the first line to #!/bin/sh and later adds [[ ... ]]. The file still ends in .sh. Why can it fail?

    ---reveal---

    The shebang selects sh, which can be dash and may not understand Bash’s [[ ... ]]. The extension does not choose the language. Restore a Bash shebang for Bash features, with #! at the very start of the first line.

  4. 4
    ExplanationRead the file with Bash first

    Run bash folder-report.sh. This starts Bash and passes it the readable script file; execute permission on the script is not required. The chosen interpreter is already Bash, so the shebang does not make this choice.

    You should see a path, a listing and the message "Report finished". The relative paths used by commands inside the script are resolved from its working directory, not automatically from the script file’s folder.

  5. 5
    ExampleAllow direct execution

    Add execute permission for the owner and run the exact path:

    chmod u+x folder-report.sh
    ./folder-report.sh
    

    The ./ names the current directory. Direct execution uses the file’s shebang. If the file is readable but not executable, the earlier bash command can work while this command fails with "Permission denied." Permission and interpreter selection are separate parts of the launch.

  6. 6
    TipFind the script by name

    Typing folder-report.sh without a slash asks Bash to search PATH. The current directory normally is not on that list.

    A personal bin directory lets you run the script from other folders when it is on PATH. Inspect echo "$PATH" and use command -v folder-report.sh to see which executable that name selects. If an old copy comes first in PATH, editing a newer copy elsewhere will not change what this name runs.

  7. 7
    ChallengeKeep a directory change in the right shell

    The script now starts with cd /var/log. After ./folder-report.sh finishes, has your terminal moved there? How could a settings file move it?

    ---reveal---

    The executed script runs in a child shell, so its directory change does not move the parent terminal. Sourcing a file with source ./settings.sh runs its commands in the current shell and can change that session’s directory. Sourcing uses the current shell’s language.

  8. 8
    ChallengeDiagnose the launch before editing commands

    A readable report works with bash folder-report.sh. Direct execution reports "Permission denied," and typing its name alone reports "command not found." What two launch issues should be checked?

    ---reveal---

    Check the script’s execute permission for direct execution. Then check PATH or use its explicit ./ path for lookup. A valid Bash shebang selects the interpreter but cannot grant permissions or place the file on PATH.

  9. 9
    RecapA launch has several separate choices

    The file holds the command sequence. The interpreter reads its language, permissions allow direct execution, and PATH or a slash locates the file.

    An executed script works in a child shell. A sourced settings file works in the shell already open, so its assignments and directory changes can remain in that session.

2

Build a dated report name without losing spaces

11 steps
Follow values through assignment, quoting, command capture and integer calculations.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionFollow one value into a command
  2. 2
    ExampleStore a multiword destination
  3. 3
    ConnectionPass the setting to a child
  4. 4
    ChallengeSeparate a name from its suffix
  5. 5
    ExamplePreserve the stored path
  6. 6
    ChallengeChoose fixed text or changing text
  7. 7
    ExampleCapture a date for the name
  8. 8
    ExampleCalculate before inserting the value
  9. 9
    ChallengeLocate the lost fraction
  10. 10
    ChallengeCombine the path and the captured value
  11. 11
    RecapDistinguish the three jobs
3

Keep a failed search from becoming a successful report

10 steps
Inspect a pipeline, preserve failure and choose the right debugging check.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionTreat the result as part of the task
  2. 2
    ExampleSee the hidden upstream failure
  3. 3
    Key InsightMake the pipeline report its failure
  4. 4
    ChallengeSave the status that matters
  5. 5
    ExampleHandle the failed work explicitly
  6. 6
    ExplanationChoose syntax checking or execution tracing
  7. 7
    ExplanationRead the error options as separate controls
  8. 8
    ChallengeExplain why this script continues
  9. 9
    ChallengeKeep the caller informed
  10. 10
    RecapRead both the data and the result
4

Inspect incoming files and read their records

15 steps
Use tests, ordered branches and loops to process files without losing names or lines.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionLet each kind of input choose its next step
  2. 2
    ExampleChoose a test for the path’s type
  3. 3
    ExplanationCompare values in the right way
  4. 4
    ChallengeInterpret a negated file test
  5. 5
    ExampleSelect an action with a command result
  6. 6
    ChallengeTrace overlapping thresholds
  7. 7
    ExplanationMatch an action word with case
  8. 8
    ExampleGive a glob loop one file per pass
  9. 9
    ChallengeExplain the empty-folder case
  10. 10
    TipChoose a variable number limit
  11. 11
    ExampleRead a record without breaking its spaces
  12. 12
    ChallengeKeep the last record without a newline
  13. 13
    ConnectionWait without continuously checking
  14. 14
    ChallengeDiagnose a total that stays zero
  15. 15
    RecapMatch the structure to the data
5

Accept file inputs and report partial failure

10 steps
Validate a reusable command, consume a flag and retain the overall result.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionMake input part of the command call
  2. 2
    ExampleInspect the call’s boundaries
  3. 3
    ExplanationConsume the leading flag
  4. 4
    ChallengeKeep an empty argument in the count
  5. 5
    ExampleReject a call with no file inputs
  6. 6
    ExplanationChoose a default only for optional input
  7. 7
    ExampleCheck the read operation itself
  8. 8
    ChallengeRemember the earlier failure
  9. 9
    ChallengeStop parsing before a hyphenated file name
  10. 10
    RecapGive reusable commands a dependable contract
6

Schedule a report and verify its last run

12 steps
Read cron expressions, build a timer pair and distinguish scheduled time from task success.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionSchedule only work that has complete input
  2. 2
    ExampleRead a personal crontab entry
  3. 3
    ChallengeTrace a minute step across an hour boundary
  4. 4
    TipGive the job an output destination
  5. 5
    ConnectionDistinguish a missed run from a failed run
  6. 6
    ExamplePut the work in a service
  7. 7
    ExamplePut the time in the matching timer
  8. 8
    ExplanationLoad changes and enable the schedule
  9. 9
    ChallengeCheck the result as well as the next time
  10. 10
    ExplanationCount time from an event when needed
  11. 11
    ChallengeChoose catch-up after downtime
  12. 12
    RecapInspect the schedule and the task separately

Shell Scripting practice questions

50 Practice Questions in 5 sets

Set 1: Trace values, records and schedules10 Practice Questions

Question 1 of 10

A script has a missing closing done. What command can detect that syntax problem without running the script?
  1. bash -x batch.sh
  2. bash -n batch.sh
  3. bash batch.sh
  4. source ./batch.sh
Show answerHide answer
Answer: B
bash -n reads the script and checks its shell structure without executing the commands. It can detect a loop that is missing its closing done. bash -x also reads the script but executes it with tracing, so earlier valid commands could run before the syntax problem is encountered.

Question 2 of 10

What are the results of [ 06 = 6 ] and [ 06 -eq 6 ], in that order?
  1. True, then false.
  2. True, then true.
  3. False, then true.
  4. False, then false.
Show answerHide answer
Answer: C
The first test compares text, and "06" contains a leading zero absent from "6", so it is false. The second compares integer values and succeeds because both represent six. Treating both tests as numeric comparisons would miss the purpose of the different operators.

Question 3 of 10

What value does left=$(( 29 % 6 )) assign?
  1. 4
  2. 4.83
  3. 5
  4. 1
Show answerHide answer
Answer: C
Four whole groups of 6 use 24, leaving a remainder of 5. The % operator returns that remainder. The quotient 4 would come from integer division with /, while Bash arithmetic does not produce a decimal quotient such as 4.83 from this expression.

Question 4 of 10

A directly executed script reports /bin/bash^M: bad interpreter, but /bin/bash exists. What file change addresses the indicated problem?
  1. Change the script extension from .sh to .bash.
  2. Move the script into a directory on PATH.
  3. Add owner execute permission to the script.
  4. Convert the script to Unix line endings.
Show answerHide answer
Answer: D
The ^M indicates a carriage return from Windows line endings, making the interpreter path differ from /bin/bash. Converting the file to Unix line endings removes that extra character. Execute permission cannot correct characters inside the interpreter path, and PATH does not resolve a fixed shebang path.

Question 5 of 10

How do you assign the text "North Hall" to a Bash variable named site?
  1. site="North Hall"
  2. site = "North Hall"
  3. $site="North Hall"
  4. site=North Hall
Show answerHide answer
Answer: A
The assignment uses the variable name without a dollar sign and has no spaces around =. Quotes preserve the two words as the value. With spaces around =, Bash treats site as a command; without quotes around the value, Hall becomes a command word instead of part of the assignment.

Question 6 of 10

A directory contains a.csv and late batch.csv, both regular files. What values are used in for f in *.csv; do echo "$f"; done?
  1. Two items: a.csv and late batch.csv.
  2. Three items: a.csv, late and batch.csv.
  3. One item: the literal pattern *.csv.
  4. One item: a.csv late batch.csv.
Show answerHide answer
Answer: A
The glob expands to matching file names as separate intact items, so the loop receives two names. The space within "late batch.csv" does not split a name produced by glob expansion. Quoting "$f" in the body keeps that value together when it is passed onward to a command.

Question 7 of 10

A file contains oak, a newline, then elm with no final newline. What count is printed?

count=0
while IFS= read -r line; do
  count=$(( count + 1 ))
done < trees.txt
echo "$count"
  1. 0
  2. 2
  3. 3
  4. 1
Show answerHide answer
Answer: D
The first read gets a newline-terminated record and enters the body, increasing the count to 1. The second stores "elm" but returns failure at end-of-file, so the body is skipped. Input redirection preserves the updated count; adding || [ -n "$line" ] would also process the final record and produce 2.

Question 8 of 10

After the call ./task.sh -v "blue room" output.txt, the script runs shift. What are $1 and $# now?
  1. -v and 2.
  2. blue room and 2.
  3. blue and 3.
  4. output.txt and 1.
Show answerHide answer
Answer: B
shift consumes the first argument, "-v", and moves the remaining two arguments down. The quoted name becomes $1, and $# falls from 3 to 2. The space inside the name stays intact; shifting does not split the text or consume the second argument as well.

Question 9 of 10

How many times per day does */25 10-12 * * * run, and when is its last run?
  1. 6 times; last at 12:25.
  2. 8 times; last at 12:55.
  3. 9 times; last at 12:50.
  4. 9 times; last at 13:00.
Show answerHide answer
Answer: C
The minute field selects 0, 25 and 50, and the hour range includes 10, 11 and 12. Three selected minutes in each of three hours gives nine runs, ending at 12:50. A continuous 25-minute count across hour boundaries would be wrong because the step restarts inside each hour.

Question 10 of 10

With default unit matching, what service is activated by inventory.timer?
  1. inventory.service
  2. timer.service
  3. inventory.timer.service
  4. inventory.target
Show answerHide answer
Answer: A
A timer normally activates the service with the same base name, so inventory.timer starts inventory.service. The timer stores the schedule, while the service defines the work. Appending another extension to the full timer name would not identify the default matching unit.
Set 2: Read and run small Bash scripts10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A script uses Bash’s [[ ... ]] syntax, but its first line is #!/bin/sh. On this machine, sh is dash. What first line selects the intended interpreter?
  1. #!/bin/sh bash
  2. # /bin/bash
  3. #!/bin/bash
  4. #!/usr/bin/env sh
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A script is readable by its owner but has no execute permission. How can the owner run its Bash commands without changing that permission?
  1. ./audit.sh
  2. bash audit.sh
  3. chmod u-x audit.sh
  4. source bash audit.sh
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

With label="west side", what arguments does printf '<%s>\n' "$label" format?
  1. One argument: west side.
  2. Two arguments: west and side.
  3. One argument: the literal $label.
  4. One argument: the literal "west side".
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

A script sets file="shift notes.txt". How should it copy that one file into the existing directory saved?
  1. cp '$file' saved/
  2. cp $file saved/
  3. cp "file" saved/
  4. cp "$file" saved/
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A command has just failed with status 7. What assignment immediately preserves that result for later use?
  1. result=$#
  2. result=$?
  3. result=$!
  4. result=0
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

The path archive exists and is a directory. What are the results of [ -f archive ] and [ -d archive ], in that order?
  1. True, then true.
  2. True, then false.
  3. False, then true.
  4. False, then false.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

What is printed by this code?

used=14
limit=20
percent=$(( used * 100 / limit ))
if [ "$percent" -ge 60 ]; then
  used=$(( used + 2 ))
elif [ "$percent" -ge 70 ]; then
  used=$(( used + 5 ))
fi
echo "$used"
  1. 16
  2. 19
  3. 21
  4. 14
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A script is called with ./label.sh "East Wing" report.txt. What are $# and $2?
  1. 3 and Wing.
  2. 2 and East Wing.
  3. 3 and report.txt.
  4. 2 and report.txt.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A noninteractive Bash script uses dir=${1:-/srv/logs}. It is called with one argument that is an empty quoted string. What value is assigned?
  1. An empty string.
  2. The literal text ${1}.
  3. /srv/logs
  4. The script exits with an error.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

In a personal crontab, what schedule does 25 4 * * * /home/rae/bin/audit.sh specify?
  1. Every day at 04:25.
  2. Every day at 25:04.
  3. Every hour at minute 4.
  4. On the 4th day at 00:25.
Show answerHide answer

Save this lesson to see this answer.

Set 3: Follow data through a complete script10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A Bash terminal starts with zone=west. The file settings.sh contains only zone=east. The terminal runs bash settings.sh, then source ./settings.sh, then bash -c 'echo "${zone:-unset}"'. The variable was never exported. What does the final command print?
  1. east
  2. unset
  3. west
  4. An empty line.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

With name=archive, what does echo "${name}_ready" print?
  1. name_ready
  2. archive
  3. An empty line.
  4. archive_ready
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A script stores completed=18 and total=32. What whole-number result is produced by $(( completed * 100 / total ))?
  1. 56
  2. 0
  3. 57
  4. 56.25
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

The file absent.log does not exist. What exit status reaches the caller?

#!/bin/bash
set -o pipefail
grep NOTICE absent.log | wc -l
status=$?
echo "Count ended"
exit "$status"
  1. 0
  2. 1
  3. 2
  4. 127
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

What is printed by this script?

set -e
false || echo "Handled"
echo "Next"
  1. Only Handled.
  2. Only Next.
  3. Handled, then Next.
  4. Nothing is printed.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

In a case block, why should *) follow the named action patterns?
  1. It matches every value and would hide later branches.
  2. It matches no value until a named pattern has failed.
  3. It repeats the previous branch for unmatched values.
  4. It applies only to file names in the current directory.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

The current directory contains only regular files draft one.jpeg, final.jpeg and notes.txt. What is the output?

count=0
for f in *.jpeg; do
  [ "$f" = final.jpeg ] && continue
  echo "${f%.jpeg}.jpg"
  count=$(( count + 1 ))
done
echo "$count"
  1. draft.jpg, then one.jpg, then 2.
  2. draft one.jpg, then final.jpg, then 2.
  3. draft one.jpeg.jpg, then 1.
  4. draft one.jpg, then 1.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A script runs until [ -f /tmp/upload.ready ]; do sleep 3; done. The regular file already exists before the loop begins. How often does the body run?
  1. Exactly once.
  2. Zero times.
  3. Endlessly.
  4. Until removed.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A batch script starts with status=0. A bad input sets status=1, and a later input succeeds. What ending reports that the request was incomplete?
  1. Set status=0 after success and use exit "$status".
  2. Keep status=1 and end with a successful echo.
  3. Set status=0 and print an error to standard output.
  4. Keep status=1 and use exit "$status".
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

New system timer and service files have been written. What command makes systemd reread their definitions before the timer is enabled?
  1. sudo systemctl daemon-reload
  2. sudo systemctl start report.service
  3. sudo systemctl list-timers
  4. sudo journalctl -u report.service
Show answerHide answer

Save this lesson to see this answer.

Set 4: Diagnose input, control flow and downtime10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A script file is renamed from weekly.sh to weekly, with contents and permissions unchanged. What does losing the extension do to Linux’s shebang-based interpreter choice?
  1. It makes Linux choose the current login shell.
  2. It makes Linux choose /bin/sh by default.
  3. It leaves the interpreter choice unchanged.
  4. It prevents Linux from reading the shebang.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A parent Bash runs export REGION=north, then starts a script that assigns REGION=south and exits. What value remains in the parent?
  1. north
  2. south
  3. An empty value.
  4. The variable becomes unset.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

In an otherwise empty directory, a script sets pattern="*.txt". What argument does touch "$pattern" receive?
  1. No path, because nothing matches.
  2. One path for every existing text file.
  3. The literal path "*.txt" with quote marks.
  4. The literal path *.txt.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

What happens in this noninteractive script?

set -eu
dest=
if [ -z "$dest" ]; then
  dest=${2:-/srv/archive}
fi
echo "$dest"

The script receives only one argument, input.txt.

  1. It stops because dest was assigned empty text.
  2. It prints /srv/archive and succeeds.
  3. It stops because the second argument is missing.
  4. It prints input.txt and succeeds.
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

The variable count contains 10, and the current directory is writable with no file named 4. What happens?

if [ "$count" > 4 ]; then
  echo "High"
else
  echo "Low"
fi
  1. It creates no file and prints High.
  2. It creates 4 and prints Low.
  3. It creates no file and prints Low.
  4. It creates 4 and prints High.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A script matches case "$value" in [0-9]*) echo "Match" ;; *) echo "No" ;; esac. What happens for value=8draft?
  1. It prints Match.
  2. It prints No.
  3. It fails because the value contains letters.
  4. It prints both Match and No.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

What is printed?

i=0
total=0
while [ "$i" -lt 4 ]; do
  i=$(( i + 1 ))
  [ "$i" -eq 2 ] && continue
  total=$(( total + i ))
  [ "$total" -gt 5 ] && break
done
echo "$i:$total"
  1. 4:10
  2. 3:4
  3. 4:8
  4. 2:1
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A script receives -v, an empty quoted string and "red room", in that order. It runs shift, then for arg in "$@"; do echo "<$arg>"; done. What is printed?
  1. <red>, then <room>.
  2. <>, then <red room>.
  3. Only <red room>.
  4. <-v>, then <>, then <red room>.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

What dates does 0 11 9 * 2 /home/rae/bin/check.sh select in ordinary cron?
  1. Only Tuesdays that are the 9th, at 11:00.
  2. Every Tuesday and every 9th, at 11:00.
  3. Every September Tuesday, at 11:00.
  4. Only the second Tuesday, at 11:09.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

An enabled calendar timer has Persistent=true. Three daily activations pass while the computer is off. What catch-up does it arrange when reactivated?
  1. One service activation for the missed calendar work.
  2. Three activations, one for each missed daily run.
  3. No activation until the next future calendar match.
  4. Continuous activations until the machine shuts down.
Show answerHide answer

Save this lesson to see this answer.

Set 5: Combine quoting, failure checks and reusable input10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A Bash terminal is in /home/rae, with PATH /home/rae/bin:/usr/local/bin:/usr/bin:/bin. Executable Bash scripts named scan exist in both bin folders and the current directory. Each contains only a Bash shebang and cd /srv/cache; that directory exists and is accessible. What file does the first command run, and what paths are printed?

scan
pwd
source ./scan
pwd
  1. /usr/local/bin/scan; then /home/rae and /srv/cache.
  2. /home/rae/bin/scan; then /home/rae and /srv/cache.
  3. ./scan; then /srv/cache and /srv/cache.
  4. /home/rae/bin/scan; then /srv/cache and /srv/cache.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A directory contains only a.txt and b.txt. With default Bash splitting and glob settings, how many data arguments are formatted by printf '<%s>\n' $value after value="*.txt spare"?
  1. One: the literal *.txt spare.
  2. Two: the literal *.txt and spare.
  3. Three: a.txt, b.txt, spare.
  4. Two: a.txt spare and b.txt spare.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A command prints ready to standard output and warning to standard error, then succeeds. What is stored by message=$(the_command)?
  1. Only ready.
  2. Only warning.
  3. Both lines in order.
  4. The command’s status 0.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

What exit status reaches the caller?

#!/bin/bash
false
saved=$?
true && echo "Handled"
exit $(( saved + 2 ))
  1. 0
  2. 1
  3. 2
  4. 3
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A script uses set -u and has already assigned folder="". It must stop with a useful diagnostic before using a missing or empty folder. What expansion supplies that check?
  1. ${folder:?Folder required}
  2. ${folder:-Folder required}
  3. "$folder"
  4. ${folder}
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

What test checks whether "$text" has nonzero length?
  1. [ -z "$text" ]
  2. [ -s "$text" ]
  3. [ -n "$text" ]
  4. [ -e "$text" ]
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

For limit=3, what values are printed by for i in {1..$limit}; do echo "$i"; done under ordinary Bash expansion?
  1. Three values: 1, 2, 3.
  2. One value: the literal {1..$limit}.
  3. No values, because the list is empty.
  4. One value: the literal {1..3}.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

What count is printed under default Bash pipeline behavior? The file contains three newline-terminated lines.

count=5
cat tasks.txt | while IFS= read -r line; do
  count=$(( count + 1 ))
done
count=$(( count + 2 ))
echo "$count"
  1. 10
  2. 7
  3. 8
  4. 5
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A script parses its inputs with this code, then requires exactly two file arguments. After the call ./copy.sh -v -- "early notes.txt" -final.txt, how many arguments remain and what should happen next?

while [ "$#" -gt 0 ]; do
  case "$1" in
    -v) verbose=1; shift ;;
    --) shift; break ;;
    -*) echo "Unknown option" >&2; exit 2 ;;
    *) break ;;
  esac
done
  1. Three remain; reject the extra end-of-options marker.
  2. Two remain; reject -final.txt as an unknown option.
  3. Two remain; validate both paths and attempt the copy.
  4. One remains; use a default for the missing destination.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A scheduled script uses Bash syntax and is executable with a Bash shebang. It works at the terminal but cron reports that a helper command is missing. The helper exists in /home/rae/.local/bin. What change addresses the likely difference?
  1. Change the script’s Bash shebang to /bin/sh.
  2. Add a user-name field to the personal crontab.
  3. Make the executable script executable a second time.
  4. Use the helper’s absolute path or set cron’s PATH.
Show answerHide answer

Save this lesson to see this answer.

Stuck on your own question? Homework Help

Key Terms

76 Key Terms, each with its definition

A

Anacron
Anacron runs periodic jobs that were missed during downtime, commonly handling daily, weekly and monthly work after startup.
Anacron can catch up a daily task on a laptop that was turned off at its usual scheduled time.
Argument count
The argument count is the number of supplied positional arguments, available as $#, with the invocation name excluded.
The argument count is 2 for ./greet.sh Ana "Lee Kim" because the quoted name is one argument.
Argument forwarding
Argument forwarding passes a script’s received values to another command, commonly using "$@" to preserve each boundary.
Argument forwarding with "$@" keeps an empty input and a multiword name as separate arguments.
Arithmetic expansion
Arithmetic expansion evaluates an integer expression inside $(( ... )) and inserts the calculated value into the command.
Arithmetic expansion changes "$(( count + 1 ))" into the next whole-number count.
Assignment
An assignment stores a value under a variable name using name=value, with no spaces around the equals sign.
The assignment name="Lee Kim" keeps the two words together as one stored value.

B

Bash
Bash is the Bourne Again Shell, a command interpreter used interactively and to execute scripts on Linux systems.
A script using [[ ... ]] selects Bash in its shebang so its conditional syntax is understood.
Brace expansion
Brace expansion creates a fixed list from notation such as {1..5} before Bash performs variable expansion on the command.
Brace expansion cannot turn {1..$n} into a range based on the value stored in n.
Branch
A branch is a group of commands selected by a condition or pattern match, giving a script different actions for different inputs.
A disk-use branch for the highest threshold must come before an overlapping warning threshold.
break
The command break exits the current loop immediately so execution continues after that loop’s closing command.
A menu loop uses break when the user enters "q" and no more menu choices should be read.

C

Calendar timer
A calendar timer matches dates and clock times through OnCalendar and can catch up missed work when Persistent is true.
A calendar timer using OnCalendar=Mon..Fri 09:00 selects weekday mornings at 09:00.
case
A case block compares one value with patterns in order and executes the commands belonging to the first matching branch.
A case block can handle "start", "stop" and an unmatched command with separate branches.
Catch-all pattern
A catch-all pattern is a pattern that matches every remaining value, usually * at the end of a Bash case block.
The catch-all pattern *) reports unknown commands after the named subcommands were checked.
Child process
A child process is a process started by another process, with its own state and inherited environment values.
A script in a child process can change directory without moving the terminal that started it.
Command substitution
Command substitution runs a command and inserts its standard output, removing trailing newlines, through the form $(command).
Command substitution in today=$(date +%F) captures a date for use in a backup file name.
Comment
A comment is explanatory text the shell ignores, introduced by # at a word boundary outside quoted text.
The comment # Keep a copy for recovery explains why the following file copy is needed.
Conditional keyword
A conditional keyword such as Bash’s [[ begins a shell test construct with its own parsing and pattern-matching rules.
The conditional keyword [[ allows [[ $name == *.log ]] without word splitting of the name.
continue
The command continue skips the remaining commands in the current loop pass and proceeds to the next pass or condition check.
A file loop uses continue after a failed guard so its processing command never sees a missing path.
Counter
A counter is a stored number updated as events or loop passes occur, commonly by adding one to its previous value.
A counter initialized with count=0 can increase once for each line that a loop reads.
cron
cron is a background scheduling service that starts commands when their crontab time fields match the current time.
A cron job can run an executable report script at 06:15 on each weekday without a terminal open.
Cron step
A cron step selects regularly spaced values within a time field, restarting from that field’s range rather than measuring elapsed time.
A cron step of */45 in minutes selects 0 and 45, then restarts at 0 in the next hour.
Crontab
A crontab is a schedule of cron entries, with five time fields and a command in each job line of a personal table.
The command crontab -l displays the current user’s crontab so its schedules can be inspected.

D

Default value
A default value supplies optional input when no usable value was given, such as ${1:-/var/log} for a missing or empty argument.
A default value lets a scheduled report use a standard directory without prompting a person.
Double quotes
Double quotes preserve spaces and wildcard characters in a value while allowing variable, command and arithmetic expansion.
Double quotes in echo "Home: $HOME *" allow the home path to expand while keeping the asterisk.

E

elif
The keyword elif introduces another condition in an if block, tested only when all earlier conditions in that block failed.
An elif test for 75% use runs only after the earlier test for at least 90% has failed.
End-of-file
End-of-file is the point where an input stream has no more data; Bash read reports failure when it encounters this condition.
At end-of-file, a final record without a newline can be stored even though read reports failure.
Environment variable
An environment variable is a named value included in the environment copied from a process to programs it starts.
The environment variable HOME supplies a home-directory path to many commands a shell launches.
errexit
errexit is Bash’s set -e option, which exits on unhandled failures while allowing specified contexts that test command status.
The errexit option allows a failed if condition to select its else branch instead of ending the script.
Execute permission
Execute permission allows an eligible user to start a file as a program, including a script whose shebang selects its interpreter.
Adding execute permission with chmod u+x job.sh lets the owner try running ./job.sh.
exit
The builtin exit ends the shell or script and reports a supplied status, or the previous command’s status when no number is given.
A script uses exit 2 after a usage error so its caller receives a failure result.
Exit status
An exit status is a whole-number command result from 0 to 255; zero means success and other values report documented outcomes.
The exit status of grep -q distinguishes a match, no match and a problem reading the input.
Export
An export marks a shell variable for inclusion in the environment inherited by programs that shell starts afterward.
An export of BACKUP_DIR lets a newly started backup script read the configured destination.

F

Failure flag
A failure flag is a stored value that remembers an unsuccessful part of a larger task so the final status remains nonzero.
A failure flag stays set after a missing file even when the script successfully counts a later file.
File test
A file test checks a path’s existence, type or access properties and returns a status that a condition can use.
The file test [ -d "$src" ] accepts a source directory but rejects a regular file.
for loop
A for loop repeats commands for each item in a list, assigning that item to its loop variable before running the body.
A for loop over web1 web2 db1 runs its body once for each of the three host names.

G

Globbing
Globbing replaces an unquoted filename pattern with matching paths; Bash normally keeps an unmatched pattern as literal text.
Globbing expands *.log into separate matching file names before a for loop begins.

I

IFS
IFS is the shell’s internal field separator variable; setting it empty for read preserves leading and trailing whitespace.
Using IFS= read -r line keeps spaces at the edges of each record read from the input file.
Integer division
Integer division calculates a whole-number quotient and discards the fractional part instead of producing a decimal result.
Integer division makes $(( 7 / 2 )) produce 3 even though the exact quotient is 3.5.
Interpreter
An interpreter is a program that reads and executes instructions written in the language it understands.
The interpreter selected by #!/usr/bin/python3 reads Python code instead of Bash commands.
Iteration
An iteration is one pass through a loop’s body, using the current list item or current values before the next pass begins.
One iteration of a file loop processes the single name "beach day.jpeg" before moving on.

J

Journal
The systemd journal stores service output and diagnostic records, allowing scheduled work to be inspected after it has run.
The journal query journalctl -u backup.service shows messages from the backup task.

L

Line ending
A line ending marks the end of a text line; Unix uses a newline, while Windows commonly adds a preceding carriage return.
A Windows line ending in a shebang can make Linux look for an interpreter path ending in ^M.
Lingering
Lingering allows a user’s systemd manager to remain available without an active login session so user services and timers can run.
An administrator enables lingering when a user’s scheduled task must continue without a login.

M

Minimal environment
A minimal environment supplies only a small set of process settings, without the variables and startup files of an interactive session.
Cron’s minimal environment can omit a personal bin directory that was available at the terminal.
Monotonic timer
A monotonic timer measures elapsed time from an event, such as boot or the last service activation, instead of matching a calendar time.
A monotonic timer can combine OnBootSec and OnUnitActiveSec for a first run and later intervals.

N

Negation
Negation reverses a command or test’s success and failure, allowing a condition to select cases where the original condition is false.
Negation in [ ! -d "$p" ] accepts a regular file as well as a path that does not exist.
nounset
nounset is Bash’s set -u option, which treats an expansion of an unset variable as an error in a noninteractive script.
With nounset enabled, a misspelled variable name can stop a script before it uses an empty path.
Numeric comparison
A numeric comparison evaluates integer values with operators such as -eq and -gt, independently of their text length.
The numeric comparison [ "$count" -ge 8 ] succeeds for a stored count of 12.

O

Option
An option is a command-line input that changes behavior, often written with a leading hyphen and consumed before ordinary arguments.
The option -v can enable extra messages before the script processes the remaining file names.

P

PATH
PATH is the colon-separated environment value listing directories searched for command names that contain no slash.
The shell searches PATH for job.sh, but follows the explicit current-directory path in ./job.sh.
pipefail
pipefail is a Bash option that makes a pipeline return its rightmost failing command’s status, or zero when every command succeeds.
With pipefail enabled, a missing input reported by grep can make the whole pipeline fail.
Pipeline status
A pipeline status is the result of a command pipeline, normally taken from its final command unless pipefail changes that rule.
The pipeline status can be zero when grep fails but the following wc successfully counts no input.
Positional parameter
A positional parameter holds an argument supplied to a script, numbered from $1, with braces required for positions after nine.
The positional parameter ${10} retrieves the tenth argument without appending a zero to $1.

Q

Quoting
Quoting controls how Bash interprets special characters and keeps intended text together while it prepares command arguments.
Quoting "$file" preserves "monthly report.txt" as one argument when copying the file.

R

read
The Bash builtin read takes a line from standard input and stores its fields in variables named in the command.
Using read -r -p "Name: " name prompts for a name and keeps backslashes in the entered text.
Remainder
A remainder is the integer amount left after whole-number division; Bash arithmetic obtains it with the % operator.
The remainder in $(( 17 % 5 )) is 2 because three complete groups of five use 15.

S

Service unit
A service unit defines work systemd should start, such as a oneshot script specified by an absolute ExecStart path.
The service unit backup.service defines the script activated by the matching backup timer.
Shebang
A shebang is the first line beginning with #! that names the interpreter Linux should use to execute a script.
The shebang #!/bin/bash starts at the first character, before any comment or blank line.
Shell script
A shell script is a plain text file of shell commands saved so an interpreter can execute them as a repeatable task.
A shell script can create a backup folder, copy notes into it and list the saved copy.
shift
The builtin shift removes leading positional parameters and renumbers the remaining ones, reducing the argument count.
After shift consumes -v, the next command-line word becomes $1 for the option parser.
Short-circuit execution
Short-circuit execution uses a command’s status to decide whether the next command in an && or || list runs.
Short-circuit execution in cd /data || exit 1 stops dependent commands after a failed directory change.
Single quotes
Single quotes enclose literal text, preventing variable, command and wildcard expansions within the quoted portion.
Single quotes make echo '$HOME' print the characters "$HOME" instead of a home path.
Sourcing
Sourcing means reading a file’s commands into the current shell so its assignments and directory changes affect that shell.
Sourcing ./settings.sh loads its variable assignments directly into the open Bash session.
Standard error
Standard error is the output stream for diagnostics, separate from standard output so normal data can be redirected independently.
A message sent to standard error with >&2 remains visible when standard output goes to a file.
String comparison
A string comparison checks text character by character, so different spellings or leading zeros can make values unequal.
A string comparison treats "07" and "7" as different even though they represent the same integer.
Suffix removal
Suffix removal is a parameter expansion that removes a matching ending from a variable’s value, as in ${f%.jpeg}.
Suffix removal turns "lake.jpeg" into "lake", so a rename command can then append ".jpg".
Syntax check
A syntax check reads a script to find malformed shell structure without executing the commands or testing their actual results.
A syntax check with bash -n detects a missing done before the loop can run any commands.

T

Timer unit
A timer unit supplies a systemd schedule that activates another unit, normally the service with the same base name.
Enabling the timer unit backup.timer establishes the schedule for the backup service.
Tracing
Tracing prints commands after expansion as Bash runs them, making the values used during execution visible for debugging.
Tracing with bash -x reveals an empty source argument passed to a file-copy command.
Trap
A trap registers a command to run when a selected signal or shell event occurs, such as EXIT when the Bash process ends.
An EXIT trap can remove the temporary file whose path was stored earlier in the script.

U

until loop
An until loop tests a command before each pass and repeats its body until that command returns success.
An until loop can sleep between checks while waiting for an export-complete file to appear.
Usage message
A usage message shows how to call a command, naming required arguments and marking optional or repeatable inputs.
The usage message "Usage: count.sh FILE..." indicates that file inputs can be repeated.

V

Validation
Validation checks whether input meets a script’s requirements before dependent work begins, including argument counts and types.
Validation rejects a directory when a counting script requires a regular file as its input.
Variable
A variable is a named value stored by the shell and expanded when a command needs to use the value associated with that name.
The variable city stores "Lisbon" after city=Lisbon, so echo "$city" can display it.
Variable expansion
Variable expansion replaces a dollar-prefixed name with its stored value while Bash prepares a command for execution.
Variable expansion turns "${file}_old" into "report_old" when file contains "report".

W

while loop
A while loop tests a command before each pass and repeats its body for as long as that command returns success.
A while loop reading a file ends when read reaches end-of-file and reports failure.
Word splitting
Word splitting divides an unquoted expansion into separate words using separator characters, normally spaces, tabs and newlines.
Word splitting turns an unquoted value "my report.txt" into two arguments before cp runs.

About this lesson

By StudeliReport a mistake

Make your own Study Materials

Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.

Get Started