You need the same list of note files more than once. Saving the listing procedure in a script makes the chosen commands visible and repeatable. Read the complete procedure and its input path before running it: a reassuring filename is not evidence about its effects.
A script is a text file containing commands for an interpreter. The first line, a shebang such as #!/bin/bash, names an interpreter for direct execution. Running bash followed by the script path selects Bash explicitly, without requiring the script's execute permission.
Before you begin
- Recommended first
- Install Ubuntu LTS and open a terminal, Where practice is allowed, Bash variables, if, and a loop
- Estimated time
- 15 min reading, 15 min activity. Setup and optional practice may take longer.
- Where you work
- Use Bash in your owned Ubuntu practice environment as an ordinary user. The supplied text files use UTF-8, a text encoding; where shown, LC_ALL=C requests consistent text ordering. Work only under your own new ~/yanosec-lab directory, never a host share or personal/system file. Refuse an existing exercise destination rather than overwriting it. No account, paid platform, or remote target is required. Optional extensions take about 10–15 minutes and are not part of the time above. A virtual machine is optional. You can also finish the reasoning from the printed evidence.
- Without a lab
- The page supplies the files, source and text records needed for the activity. Without a shell, follow the numbered steps on paper, label each result as predicted, and use the separate evidence supplied by the question. Running commands can wait until your lab is available. Reading works without JavaScript. Answer checking needs JavaScript and a secure context, such as HTTPS; saving a correct result also needs usable browser storage. Missing tools, managed or mobile hosts, or a deferred guest setup never require buying a service. Keep owned lab files for review; do not use broad deletion.
After this lesson you can:
- Save and inspect a small Bash script before running it.
- Explain its directory argument and read-only listing behavior.
- Compare repeatable output on unchanged controlled input and record limitations.
01Example
This smaller worked tree contains a.txt and b.log. find selects regular files whose names match *.txt and displays the matching path. It does not read the text inside those files. The full script adds input checks and sorting for a separate supplied tree.
Worked tree:
./demo-notes/a.txt
./demo-notes/b.log
Command:
find ./demo-notes -type f -name '*.txt'
Predicted listing:
./demo-notes/a.txt
This expression has no -delete, -exec or output-file redirection.02Read the whole source and its limits
An argument is a value supplied after the script name. $1 expands its first argument; $# gives the number of arguments. The supplied script accepts exactly one directory path. A guard is a check that stops processing when a required condition is absent.
The case statement accepts paths beginning / or ./. The first is absolute; the second explicitly starts from the working directory. The script then checks that the path names a directory. These checks expose common mistakes, but they do not prove ownership or confine an arbitrary path to your lab.
The final find selects regular files with names ending .txt. Its -print action displays each matching path. sort orders those lines. LC_ALL=C requests a consistent locale for that sort operation; a locale supplies conventions such as text ordering. It does not change the file bytes.
set -o pipefail makes a Bash pipeline report failure if a component fails instead of reporting only the last command's success. It helps expose a failed find followed by a successful sort. It is an error-reporting setting, not a sandbox or permission grant.
The listing is designed for this controlled tree, including one filename containing a space. A filename containing a newline could be confused with two output lines. Changing files or permissions can also change results. Use only the supplied owned tree and do not infer a general-purpose safe processor from this small example.
Filter the supplied regular-file names without mutating the tree; the source has no delete, exec or output-file redirection.Model. A teaching model drawn by hand. It shows the idea, not real tool output.
Read the full explanation below.
Both regular files are inside ./demo-notes.
Immutable supplied teaching corpus
| Source path | Type |
|---|---|
| ./demo-notes/a.txt | regular file |
| ./demo-notes/b.log | regular file |
Result
./demo-notes/a.txt regular file
./demo-notes/b.log regular fileThe fixed supplied tree contains both a.txt and b.log.
Follow the explanation
- The fixed supplied tree contains both a.txt and b.log.
- Only a.txt matches the filename predicate; the predicted listing is ./demo-notes/a.txt.
Read every state and value
1. The fixed supplied tree contains both a.txt and b.log.
- View
- source
- Rows shown
- find ./demo-notes -type f -name '*.txt' · ./demo-notes/b.log · regular file
- Highlighted Cell Refs
- Row: find ./demo-notes -type f -name '*.txt'; Column: Type · Row: ./demo-notes/b.log · regular file; Column: Type
- Result rows
- Source Row: find ./demo-notes -type f -name '*.txt'; Cells: ./demo-notes/a.txt · regular file · Source Row: ./demo-notes/b.log · regular file; Cells: ./demo-notes/b.log · regular file
2. Only a.txt matches the filename predicate; the predicted listing is ./demo-notes/a.txt.
- View
- find ./demo-notes -type f -name '*.txt'
- Rows shown
- find ./demo-notes -type f -name '*.txt'
- Highlighted Cell Refs
- Row: find ./demo-notes -type f -name '*.txt'; Column: Source path
- Result rows
- Source Row: find ./demo-notes -type f -name '*.txt'; Cells: ./demo-notes/a.txt
03Use a fresh owned workspace
The paths lesson introduced ~/yanosec-lab: ~ expands to your home directory. Before a file activity, return with cd ~/yanosec-lab. Use pwd to check your location and ls -a to inspect names. Continue only if this is your recognized owned lab. Then create the lesson's new child directory.
If mkdir reports that a destination already exists, preserve that work. Choose an unused name and use it consistently, or follow the paper route. Enter one command at a time and continue only after the preceding step succeeds. Record an unexpected result before doing anything else.
Unless the lesson says otherwise, leave the small activity folder for review and return with cd ~/yanosec-lab when finished. Closing the terminal does not delete files you saved. These activities need no sudo or system-file changes.
04Decode the guards
In [ "$#" -ne 1 ], -ne means the numbers are not equal. The error branch therefore runs when the argument count is not one. In [ ! -d "$1" ], -d tests whether the path is a directory and ! reverses that result.
The case alternatives /* and ./* match the accepted path beginnings. A nonmatching path reaches the usage-error branch. The script writes diagnostics to stderr using >&2; the 2 names the standard-error stream. exit 2 then ends the script with its chosen nonzero status.
Read both successful and error paths before running it. The guards can print messages and stop, while the successful final pipeline lists paths. No deletion, content replacement or program execution action is applied to the input tree.
05Try the owned note list
The brief prints every filename and its full content, plus a retained earlier listing recorded with Bash 5.2.21. The optional archive holds that same tree. Read the extraction guide first if using it, and choose a new automation destination.
For the paper route, trace the one argument through the guards, select only the .txt entries, and sort their complete paths. Repeat from unchanged inputs and compare the two lists. This practices reading the script while saving and executing it remain deferred.
#!/bin/bash
# Original read-only chore.
# Provide exactly one owned directory argument, with ./ or an absolute path.
set -o pipefail
if [ "$#" -ne 1 ]; then
printf '%s\n' 'Usage: bash list-notes.sh OWNED-DIRECTORY' >&2
exit 2
fi
case "$1" in
/*|./*) ;;
*) printf '%s\n' 'Use an absolute path or a path beginning ./.' >&2; exit 2 ;;
esac
if [ ! -d "$1" ]; then
printf '%s\n' 'Directory not found.' >&2
exit 2
fi
find "$1" -type f -name '*.txt' -print | LC_ALL=C sort
Review the note-list script before running it
$1 is the first argument and $# counts arguments. The guards require
one explicit directory path; they do not prove ownership. find selects
regular .txt names and sort orders their paths. This is a read-only
listing of the supplied controlled tree, not a deletion procedure.
The output below is retained from an earlier Bash 5.2.21 exercise.
Scope: one owned supplied tree, no network, no elevated privilege.
Inspect script and all inputs before running. Run with Bash, not sh.
owned-tree/notes/alpha.txt = first note (newline)
owned-tree/notes/meeting note.txt = second note (newline)
owned-tree/notes/archive.log = log only (newline)
bash list-notes.sh ./owned-tree
Observed stdout with Bash 5.2.21, ordered by C locale:
./owned-tree/notes/alpha.txt
./owned-tree/notes/meeting note.txt
Running twice on unchanged input should print the same lines.
This format is for this controlled text tree, not filenames with newlines.
The script lists recursively, including dot-named .txt files if supplied.
It does not follow encountered symlink directories by find's default.
Question script's only command: find "$1" -type f -name '*.txt'
Does this command list or delete files? Enter list or delete.
Read an owned archive before extracting it
An archive bundles named entries. Extracting writes those entries back
to disk. A .tar.gz file uses tar bundling plus gzip compression; neither
operation encrypts the content. Use only the supplied owned archives.
All supplied archives contain original ordinary text files under one
named directory. No symlinks, executables with special modes, devices,
absolute paths or parent traversal entries are authored. Inspect first.
Save the chosen archive in the GUEST Downloads folder. If its actual
location differs, use that quoted absolute path; do not guess.
Use a new destination beneath ~/yanosec-lab, never an existing folder.
Example templates (replace ARCHIVE.tar.gz and NEW-DEST):
tar -tzf ~/Downloads/ARCHIVE.tar.gz
mkdir ~/yanosec-lab/NEW-DEST
tar -xzf ~/Downloads/ARCHIVE.tar.gz -C ~/yanosec-lab/NEW-DEST
Run the extraction only if mkdir succeeded and inventory matches.
t lists; x extracts; z handles gzip; f names archive; -C names target.
Keep original data; do not use sudo, overwrite flags or root extraction.
Stop on an unexpected path/link/member or download you cannot identify.
Fallback: read the full inline tree and contents; predict actions in notes.
Cleanup: leave scoped files for review; restore a guest baseline only
if all subsequent guest work is disposable. No broad rm command.
- From the lab root, list tar -tzf ~/Downloads/owned-tree.tar.gz. Create mkdir automation; after success extract with tar -xzf ~/Downloads/owned-tree.tar.gz -C ~/yanosec-lab/automation and enter cd automation. Expected root: owned-tree with two .txt notes and one .log file. Substitute a quoted actual download path if needed.
- Save the visible script into this directory as list-notes.sh using the editor procedure, or place the downloaded source there. Inspect cat list-notes.sh completely; explain guards, find, sorting and every possible modifying operation (none to the tree).
- Run bash list-notes.sh ./owned-tree. Expected two sorted paths: ./owned-tree/notes/alpha.txt then ./owned-tree/notes/meeting note.txt. The .log input is excluded by name.
- Run the same command again without changing inputs. Expected same list; record actual stdout/stderr/status privately rather than calling the prediction verified. Empty, missing or invalid input uses the shown guards, not a broader fallback search.
- On paper trace both unchanged runs from the exact source and inputs. Return to the lab root and retain files. No executable-mode, sudo, remote session or deletion is needed.
06Reach and automation module case
The optional note-list-case archive contains the same two-note tree and script under its own root. The automation case supplies its complete text alternative. Combine the editor procedure, a syntax-only SSH note and two unchanged list predictions.
Check your note: identify the saved reviewed source, one explicit quoted owned directory argument, and two matching text-note paths in stable order. Explain error handling and keep the SSH fingerprint pending. Neither the .log nor the .sh script is a .txt note. State which practical steps you actually performed; this written synthesis requires no remote connection.
Combine saved source, arguments and a connection plan
Use the complete tree below and the visible script source. Trace each
guard, then predict the same ordered list twice without changing input.
The SSH label is for syntax only; leave the real fingerprint pending.
Use the rubric to explain what you can establish and what practical
steps remain deferred.
Original case: note-list-case has notes/alpha.txt, notes/meeting note.txt,
notes/archive.log, plus the same authored list-notes.sh.
Use editor to save script from visible source, read it before execution,
trace argument/if/case/find/sort, and predict two unchanged runs.
SSH card: port 2222, user learner, assigned-host label, fingerprint pending.
Record syntax and authorization checks without contacting this label.
Rubric: one scoped read-only script, saved source, ordered two-note list,
quoted directory argument, no key/password disclosure or network action.
07Optional challenge
Allow about ten extra minutes. Add another .txt filename containing a space on paper and predict its sorted position. Explain why a quoted input path preserves spaces, while newline-separated output still cannot represent every possible filename unambiguously. Keep this as a listing exercise.
08Fresh cases for the checks
Use these fictional cases for the checks below. They have different inputs from the worked examples. Read them here or make notes; no installation or external service is needed.
Script card A: complete tiny script and controlled inputOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.
Complete saved source of show-notes.sh:
#!/bin/bash
find "$1" -type f -name '*.txt' -print | LC_ALL=C sort
Invocation to read, not execute: bash show-notes.sh "./review notes"
This smaller script has no argument guards. Assume exactly this one owned, readable directory argument.
Complete input tree (no symlinks or filenames containing newlines):
./review notes/agenda.txt regular file
./review notes/room map.txt regular file
./review notes/history.log regular file
./review notes/archive directory
./review notes/archive/old.txt regular file
./review notes/template.txt empty directory
All parent paths are readable/traversable. No file changes or errors occur during this prediction.
Script card B: independent repeatability reportsOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.
A reviewed script lists regular .txt paths recursively and sorts complete paths with LC_ALL=C.
Run R and run S use the same script bytes, argument, readable tree, names and file types.
Before run T, an additional regular file called extra.txt is added inside that same tree.
All runs finish without errors. No newline-containing filenames occur.
The script prints paths, not file contents. No result is an execution record for your machine.
Script card C: independent changed selectionOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.
Complete source after changing only the name pattern from *.txt to *.log:
#!/bin/bash
find "$1" -type f -name '*.log' -print | LC_ALL=C sort
Invocation: bash show-log.sh ./daybook
Complete readable owned tree:
./daybook/idea.txt regular file
./daybook/run.log regular file
./daybook/empty.log empty directory
No errors, symlinks or filenames containing newlines occur. Predict only; do not execute.
09Explain a repeatable listing
Optional self-check: describe what you would inspect before saving and running a note-list script in an owned exercise directory. Explain its argument, what it prints and why two runs might differ.
Review your explanation: inspect the complete saved source and input path; keep a spaced path in one quoted argument; distinguish regular-file/name tests from reading contents or modifying files; identify the sorting rule; and qualify repeatability with unchanged inputs, access and error-free completion. The small assessment script omits the lesson script's guards, so do not assume it validates arbitrary inputs or proves ownership. Mark paper predictions and deferred execution. This explanation is self-reviewed.
10Common mistakes
If the script refuses an argument, inspect argument count and the required ./ or absolute prefix. Do not remove guards to accept a guessed or out-of-scope path.
If Bash reports parsing trouble, inspect interpreter, straight quotes and LF versus CRLF line endings (LF is the Linux-style newline; CRLF is the two-character convention often used on Windows). Use the visible source fallback; do not change host policy or execute permissions to repair syntax.
If output differs across runs, check whether files or read permissions changed and whether stderr reports a failure. A stable controlled case is not a guarantee for arbitrary live trees.
References
- Ubuntu 24.04 packaged manual: bash(1)
Shell grammar, quoting, builtins, redirection and job control; use only the sections introduced in this lesson. Use the named command and option sections. This manual describes Ubuntu 24.04 packages; your installed version can differ. The lesson supplies the facts needed for its activity.
- Ubuntu 24.04 packaged manual: find(1)
Use the named command and option sections. This manual describes Ubuntu 24.04 packages; your installed version can differ. The lesson supplies the facts needed for its activity.
- Ubuntu 24.04 packaged manual: sort(1)
Use the named command and option sections. This manual describes Ubuntu 24.04 packages; your installed version can differ. The lesson supplies the facts needed for its activity.