Computing Foundations › Inside the Machine › Day 5
Hands-on lab — Day 5: Text, Images, and Sound as Data
- ← Back to the Day 5 lesson
- Open the hands-on files on GitHub — clone or download them from the public labs repository
- Local path in your clone:
labs/sections/computing-foundations/day-005-text-images-and-sound-as-data/
Commands
Setup
cd labs/sections/computing-foundations/day-005-text-images-and-sound-as-data Run
bash examples/inspect_bytes.sh
bash starter/inspect_bytes.sh Test
bash tests/run_tests.sh File tree
examples/inspect_bytes.sh examples/make_samples.sh examples/samples/hello.txt examples/samples/tiny.png examples/samples/unicode.txt expected-output/inspect-bytes-macos.txt expected-output/NOTES.md expected-output/test-run-macos.txt metadata.yml README.md requirements/README.md security.md starter/byte-detective-worksheet.md starter/inspect_bytes.sh tests/run_tests.sh troubleshooting.md
Lab README
Day 005 lab — X-Ray Your Files: Seeing Bytes with hexdump
Lesson
- Lesson title: Text, Images, and Sound as Data
- Day number: 5 of 365
- Lesson article: https://ai-roadmap-365.github.io/day-005-text-images-and-sound-as-data
- Lab files: everything you need is in this directory — follow “How to run” below.
- Browse the course locally: from the repository root, this lab also appears in the course website at
/labs/day-005-text-images-and-sound-as-datawhen the site is running.
Purpose
Day 5's lesson claims that text, images, and sound are nothing but bytes
under agreed conventions. This lab lets you verify that claim with your
own eyes: you point xxd and file at three small committed sample
files — pure ASCII text, UTF-8 text with accents and an emoji, and a
real 145-byte PNG image — and read the actual bytes: ASCII codes,
multi-byte UTF-8 sequences, and the PNG magic bytes 89 50 4E 47.
Learning objectives
- Read a hex dump: offsets on the left, bytes in the middle, the ASCII interpretation on the right.
- Locate 1-, 2-, 3-, and 4-byte UTF-8 sequences in a real file and explain why byte count and character count differ.
- Identify a file format from its magic bytes and verify it with
file. - Demonstrate that a file's extension is a naming convention while its content determines its true type.
- Complete a small shell script by filling in four well-specified exercises, and answer a worksheet from observed bytes only.
Prerequisites
- The Day 5 lesson (read it first — it explains every byte you will see).
- Comfort running commands in a terminal (Day 1 lab).
- No programming experience required; every command is given and explained.
Supported operating systems
- macOS — fully supported (tested on macOS with Apple Silicon).
- Linux — fully supported (any distribution with
xxdandfile; installxxdfrom your package manager if a minimal image lacks it). - Windows — run the scripts unmodified inside WSL; PowerShell users
can explore manually with
Format-Hex, but the tests assume a Unix-style shell.
Hardware requirements
Any computer. The samples total 191 bytes; the lab only reads them and writes to a throwaway temp directory.
Required software
bash(3.2 or newer — preinstalled on macOS and Linux).xxd(preinstalled on macOS; ships with vim on Linux) andfile(preinstalled everywhere).hexdump -Cworks as a stand-in forxxd.
Free and open-source options
Everything in this lab is free: bash, xxd, hexdump, and file are
open-source or ship with your OS. No account, API key, or download is
needed — the sample files are committed to the repository, and
examples/make_samples.sh can rebuild them offline at any time.
Installation
None. From the repository root:
cd labs/sections/computing-foundations/day-005-text-images-and-sound-as-data
File structure
day-005-text-images-and-sound-as-data/
├── README.md ← you are here
├── metadata.yml ← machine-readable lab metadata
├── starter/
│ ├── inspect_bytes.sh ← YOUR working file (4 exercises)
│ └── byte-detective-worksheet.md ← 10 questions answered from real bytes
├── examples/
│ ├── inspect_bytes.sh ← completed reference walkthrough
│ ├── make_samples.sh ← rebuilds the samples byte by byte
│ └── samples/
│ ├── hello.txt ← 14 bytes of pure ASCII
│ ├── unicode.txt ← UTF-8: é (2 bytes), € (3), emoji (4)
│ └── tiny.png ← real 145-byte PNG, 2×2 pixels
├── tests/
│ └── run_tests.sh ← automated checks
├── expected-output/
│ ├── inspect-bytes-macos.txt ← real captured walkthrough run
│ ├── test-run-macos.txt ← real captured test run
│ └── NOTES.md ← what must match vs. may differ
├── requirements/
│ └── README.md ← dependency statement
├── troubleshooting.md
└── security.md
How to run
From this directory:
## 1. Watch the finished walkthrough first
bash examples/inspect_bytes.sh
## 2. Your task: complete the four exercises in the starter, then run it
bash starter/inspect_bytes.sh
## 3. Fill in starter/byte-detective-worksheet.md from what you observed
## 4. Check your work
bash tests/run_tests.sh
What the commands do
bash examples/inspect_bytes.sh— for each sample file, runsfile(identify by content),wc -c/wc -m(bytes vs. characters), andxxd(hex dump), with printed notes pointing at the interesting bytes: ASCII codes inhello.txt, the sequencesc3 a9,e2 82 ac, andf0 9f 8e 89inunicode.txt, and the PNG signature plus readable chunk names intiny.png. Section 4 copies the samples under swapped extensions into a temp directory and showsfilejudging content, not names.bash starter/inspect_bytes.sh— the same investigation as a skeleton: four placeholder lines, each preceded by a comment naming the exact command to put there (xxd,xxd -l 8, and acp … && file …line).bash examples/make_samples.sh— optional; rebuilds the three samples deterministically (printfbyte escapes for the text files, base64 decoding for the PNG) and printsfile's verdict on each.bash tests/run_tests.sh— checks that the samples exist and are identified correctly byfile, thatxxdshows the exact expected byte sequences (PNG signature89504e470d0a1a0a, the UTF-8 sequences, the exact 14 bytes ofhello.txt), that content beats extension, and that both scripts run cleanly.
Expected output
See expected-output/inspect-bytes-macos.txt
for the full real captured run. The heart of it:
00000000: 4865 6c6c 6f2c 2077 6f72 6c64 210a Hello, world!.
00000000: 6361 66c3 a920 636f 7374 7320 3320 e282 caf.. costs 3 ..
00000010: ac0a 72c3 a973 756d c3a9 0af0 9f8e 890a ..r..sum........
00000000: 8950 4e47 0d0a 1a0a .PNG....
Your output must show the same bytes — bytes do not vary by machine.
Only file's phrasing and locale-dependent character counts may differ
slightly (see expected-output/NOTES.md).
Validation steps
- Run
bash starter/inspect_bytes.sh— it must exit without errors and no longer print anynot completed yetline. - In your own output, point at: the byte
48(H), the pairc3 a9(é), and the four bytes89 50 4e 47(PNG signature). - Confirm the worksheet has all 10 answers filled in, each backed by a command you actually ran.
- Run the tests (next section) — all checks must pass.
Tests
bash tests/run_tests.sh
Expected final line: 18 checks, 0 failure(s). while the starter still
has placeholders (structure-only check), rising to 20 checks, 0 failure(s). once you have completed all four exercises. The command
exits 0 on success and non-zero on any failure, so it can run in CI.
Cleanup
Nothing to clean up: the scripts write only inside mktemp -d scratch
directories, which they remove themselves. To reset your work:
git checkout -- starter/inspect_bytes.sh starter/byte-detective-worksheet.md.
Troubleshooting
See troubleshooting.md for the full list (missing
xxd, locale-dependent wc -m, emoji rendering, file wording
differences, corrupted PNG after line-ending translation, BOM bytes).
Security notes
See security.md. Short version: read-only inspection, no network, no privileges — and the rename experiment is itself a security lesson: extensions are claims, magic bytes are evidence.
Extension exercises
- Hex-dump other real files on your machine with
xxd file | head— a JPEG photo (startsff d8 ff), a PDF (25 50 44 46, which spells%PDF), a ZIP archive (50 4b, "PK"). Keep a small table of the magic bytes you find. - Use
xxd -b examples/samples/hello.txt | head -3to see the same bytes as raw binary, and check thatHis01001000(72). - Save the same short sentence from your editor as UTF-8 and as UTF-16,
dump both, and explain the size difference and the
ff febyte-order mark you will find. - Run
strings examples/samples/tiny.pngand compare with the full hex dump: which parts of a binary file doesstringssurface, and why is it a favorite first tool for inspecting unknown binaries?
Navigation
- Previous day: Day 4 — Binary and Data Representation: Bits, Bytes, and Numbers (
../day-004-binary-and-data-representation-bits-bytes/) - Next day: Day 6 — Operating Systems: What They Do and Why (
../day-006-operating-systems-what-they-do-and/)
Expected output
NOTES.md
# Expected output — what must match and what may differ
Both files in this directory were captured from real runs on macOS
(Apple Silicon).
## Files
- `inspect-bytes-macos.txt` — full output of `bash examples/inspect_bytes.sh`.
- `test-run-macos.txt` — full output of `bash tests/run_tests.sh`
(final line: `18 checks, 0 failure(s).` with the starter untouched).
## Must be identical on every platform
The **bytes** never change — they are the whole point of the lab:
- `hello.txt` dump: `4865 6c6c 6f2c 2077 6f72 6c64 210a` (14 bytes).
- `unicode.txt` dump: contains `c3 a9` (é), `e2 82 ac` (€), and
`f0 9f 8e 89` (party-popper emoji); 32 bytes, 24 characters.
- `tiny.png` first 8 bytes: `8950 4e47 0d0a 1a0a`; 145 bytes total.
## May differ slightly by platform
- The wording of `file` output: e.g. Linux may print `Unicode text, UTF-8
text` as just `UTF-8 text`, or append `, with no line terminators` for
some files. The tests only require the substrings `ASCII text`, `UTF-8`,
and `PNG image data`.
- `wc -m` requires a UTF-8 locale to count 24 characters; in a `C`/POSIX
locale it counts bytes (32). See `troubleshooting.md`.
- Temporary-directory paths in section 4 of the walkthrough are hidden
because the script prints bare file names.
- Once you complete all four starter exercises, the test count rises from
18 to 20 checks (the strict checks replace the structure-only check).
inspect-bytes-macos.txt
=== 1. hello.txt — plain ASCII, one byte per character ===
hello.txt: ASCII text
size in bytes: 14
size in characters: 14
00000000: 4865 6c6c 6f2c 2077 6f72 6c64 210a Hello, world!.
Note: 13 visible characters + 1 newline (0a) = 14 bytes.
Note: 'H' = 48 hex = 72 decimal, exactly its ASCII code.
=== 2. unicode.txt — UTF-8 with 1-, 2-, 3-, and 4-byte characters ===
unicode.txt: Unicode text, UTF-8 text
size in bytes: 32
size in characters: 24
00000000: 6361 66c3 a920 636f 7374 7320 3320 e282 caf.. costs 3 ..
00000010: ac0a 72c3 a973 756d c3a9 0af0 9f8e 890a ..r..sum........
Note: bytes and characters differ because some characters need
several bytes. Look for these multi-byte UTF-8 sequences above:
c3 a9 = e-acute (U+00E9, 2 bytes)
e2 82 ac = euro sign (U+20AC, 3 bytes)
f0 9f 8e 89 = party emoji (U+1F389, 4 bytes)
xxd prints a dot in the right-hand column for every byte that is
not printable ASCII — that is why the accents look like dots.
=== 3. tiny.png — a real binary file with magic bytes ===
tiny.png: PNG image data, 2 x 2, 8-bit/color RGB, non-interlaced
size in bytes: 145
first 8 bytes (the PNG signature):
00000000: 8950 4e47 0d0a 1a0a .PNG....
89 50 4e 47 0d 0a 1a 0a — bytes 2-4 spell 'PNG' in ASCII.
full dump (note the readable chunk names IHDR, tEXt, IDAT, IEND):
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
00000010: 0000 0002 0000 0002 0802 0000 00fd d49a ................
00000020: 7300 0000 3a74 4558 7443 6f6d 6d65 6e74 s...:tEXtComment
00000030: 0044 6179 2035 2073 616d 706c 653a 2032 .Day 5 sample: 2
00000040: 7832 2070 6978 656c 7320 2d20 7265 642c x2 pixels - red,
00000050: 2067 7265 656e 2c20 626c 7565 2c20 7768 green, blue, wh
00000060: 6974 6594 647d 7b00 0000 1249 4441 5478 ite.d}{....IDATx
00000070: da63 f8cf c0c0 00c2 0cff 8100 001f ee05 .c..............
00000080: fbf1 abba 7700 0000 0049 454e 44ae 4260 ....w....IEND.B`
00000090: 82 .
=== 4. Extensions lie; magic bytes do not ===
Renamed hello.txt -> hello.png. What does 'file' say?
hello.png: ASCII text
Renamed tiny.png -> tiny.txt. What does 'file' say?
tiny.txt: PNG image data, 2 x 2, 8-bit/color RGB, non-interlaced
'file' reads the CONTENT (magic bytes), not the name: the fake
.png is still ASCII text, and the fake .txt is still PNG image data.
=== End of byte walkthrough ===
test-run-macos.txt
Checking sample files exist ...
ok: samples/hello.txt exists and is non-empty
ok: samples/unicode.txt exists and is non-empty
ok: samples/tiny.png exists and is non-empty
Checking 'file' identifies each sample by content ...
ok: hello.txt is ASCII text
ok: unicode.txt is UTF-8 text
ok: tiny.png is PNG image data
Checking the exact bytes with xxd ...
ok: tiny.png starts with the PNG magic bytes 89 50 4e 47 0d 0a 1a 0a
ok: unicode.txt contains the 2-byte sequence c3 a9 (e-acute)
ok: unicode.txt contains the 3-byte sequence e2 82 ac (euro sign)
ok: unicode.txt contains the 4-byte sequence f0 9f 8e 89 (emoji)
ok: hello.txt is exactly the 14 ASCII bytes of 'Hello, world!' + newline
Checking content beats extension ...
ok: 'file' still sees PNG content behind a .txt name
Running the reference walkthrough (examples/inspect_bytes.sh) ...
ok: reference walkthrough exits successfully
ok: walkthrough output shows the PNG magic bytes
ok: walkthrough output shows the e-acute bytes
ok: walkthrough shows 'file' identifying the PNG
Running your starter script (starter/inspect_bytes.sh) ...
ok: starter script exits successfully
Note: starter still has unfinished exercises — structure checked only.
ok: starter prints its header
18 checks, 0 failure(s).
Source files
examples/inspect_bytes.sh (2362 bytes)
#!/usr/bin/env bash
# Day 005 lab — completed reference walkthrough: X-ray the three sample
# files with `file`, `wc`, and `xxd` and point at what the bytes mean.
#
# Run from anywhere: bash examples/inspect_bytes.sh
set -euo pipefail
samples="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/samples"
cd "${samples}" # so every tool prints short, machine-neutral file names
section() {
echo
echo "=== $1 ==="
}
section "1. hello.txt — plain ASCII, one byte per character"
file hello.txt
echo "size in bytes: $(wc -c < hello.txt | tr -d ' ')"
echo "size in characters: $(wc -m < hello.txt | tr -d ' ')"
xxd hello.txt
echo "Note: 13 visible characters + 1 newline (0a) = 14 bytes."
echo "Note: 'H' = 48 hex = 72 decimal, exactly its ASCII code."
section "2. unicode.txt — UTF-8 with 1-, 2-, 3-, and 4-byte characters"
file unicode.txt
echo "size in bytes: $(wc -c < unicode.txt | tr -d ' ')"
echo "size in characters: $(wc -m < unicode.txt | tr -d ' ')"
xxd unicode.txt
echo "Note: bytes and characters differ because some characters need"
echo "several bytes. Look for these multi-byte UTF-8 sequences above:"
echo " c3 a9 = e-acute (U+00E9, 2 bytes)"
echo " e2 82 ac = euro sign (U+20AC, 3 bytes)"
echo " f0 9f 8e 89 = party emoji (U+1F389, 4 bytes)"
echo "xxd prints a dot in the right-hand column for every byte that is"
echo "not printable ASCII — that is why the accents look like dots."
section "3. tiny.png — a real binary file with magic bytes"
file tiny.png
echo "size in bytes: $(wc -c < tiny.png | tr -d ' ')"
echo "first 8 bytes (the PNG signature):"
xxd -l 8 tiny.png
echo "89 50 4e 47 0d 0a 1a 0a — bytes 2-4 spell 'PNG' in ASCII."
echo "full dump (note the readable chunk names IHDR, tEXt, IDAT, IEND):"
xxd tiny.png
section "4. Extensions lie; magic bytes do not"
workdir="$(mktemp -d)"
cp "${samples}/hello.txt" "${workdir}/hello.png"
cp "${samples}/tiny.png" "${workdir}/tiny.txt"
echo "Renamed hello.txt -> hello.png. What does 'file' say?"
(cd "${workdir}" && file hello.png)
echo "Renamed tiny.png -> tiny.txt. What does 'file' say?"
(cd "${workdir}" && file tiny.txt)
echo "'file' reads the CONTENT (magic bytes), not the name: the fake"
echo ".png is still ASCII text, and the fake .txt is still PNG image data."
rm -rf "${workdir}"
echo
echo "=== End of byte walkthrough ==="
examples/make_samples.sh (1770 bytes)
#!/usr/bin/env bash
# Day 005 lab — (re)build the three sample files in examples/samples/.
#
# The samples ship with the repository, so you normally never need to run
# this. It exists so you can see that every byte in the samples is
# deliberate: the text files are written byte by byte with printf escape
# sequences, and the PNG is decoded from base64 text back into its exact
# 145 binary bytes.
set -euo pipefail
samples_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/samples"
mkdir -p "${samples_dir}"
# 1. hello.txt — pure ASCII: every character is exactly one byte.
printf 'Hello, world!\n' > "${samples_dir}/hello.txt"
# 2. unicode.txt — UTF-8 with 1-, 2-, 3-, and 4-byte characters:
# \xc3\xa9 = é (U+00E9, 2 bytes)
# \xe2\x82\xac = € (U+20AC, 3 bytes)
# \xf0\x9f\x8e\x89 = party-popper emoji (U+1F389, 4 bytes)
printf 'caf\xc3\xa9 costs 3 \xe2\x82\xac\nr\xc3\xa9sum\xc3\xa9\n\xf0\x9f\x8e\x89\n' \
> "${samples_dir}/unicode.txt"
# 3. tiny.png — a real, valid 145-byte PNG: a 2x2 image whose pixels are
# red, green, blue, and white, plus a tEXt chunk holding a comment.
# The binary bytes are stored here as base64 text and decoded back.
png_b64='iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAOnRFWHRDb21tZW50AERheSA1IHNhbXBsZTogMngyIHBpeGVscyAtIHJlZCwgZ3JlZW4sIGJsdWUsIHdoaXRllGR9ewAAABJJREFUeNpj+M/AwADCDP+BAAAf7gX78au6dwAAAABJRU5ErkJggg=='
if printf %s "${png_b64}" | base64 -d > "${samples_dir}/tiny.png" 2>/dev/null; then
: # GNU coreutils and modern macOS accept -d
else
printf %s "${png_b64}" | base64 -D > "${samples_dir}/tiny.png" # older macOS uses -D
fi
echo "Samples written to ${samples_dir}:"
file "${samples_dir}/hello.txt" "${samples_dir}/unicode.txt" "${samples_dir}/tiny.png"
examples/samples/hello.txt (14 bytes)
Hello, world!
examples/samples/tiny.png (145 bytes)
�PNG
IHDR �Ԛs :tEXtComment Day 5 sample: 2x2 pixels - red, green, blue, white�d}{ IDATx�c���� ��� ���w IEND�B`� examples/samples/unicode.txt (32 bytes)
café costs 3 €
résumé
🎉
metadata.yml (609 bytes)
lesson_id: D005
day: 5
kind: encoding-inspection
languages: [bash]
setup_commands:
- cd labs/sections/computing-foundations/day-005-text-images-and-sound-as-data
run_commands:
- bash examples/inspect_bytes.sh
- bash starter/inspect_bytes.sh
test_commands:
- bash tests/run_tests.sh
cleanup_commands:
- 'git checkout -- starter/inspect_bytes.sh starter/byte-detective-worksheet.md # optional: reset your work'
requires_network: false
requires_api_key: false
estimated_minutes: 30
last_executed: '2026-07-12'
executed_on: 'macOS (Apple Silicon), bash tests/run_tests.sh → 18 checks, 0 failure(s).'
requirements/README.md (936 bytes)
# Dependencies — Day 005 lab
**None beyond a POSIX shell and standard utilities.** Everything this lab
uses ships with macOS and mainstream Linux distributions:
- `bash` ≥ 3.2 (preinstalled on macOS and Linux)
- `xxd` — the hex-dump tool (ships with vim; preinstalled on macOS, and
present on nearly every Linux distribution via the `vim`/`vim-common`
or `xxd` package)
- `file` — identifies file types by magic bytes (preinstalled everywhere)
- `wc`, `cp`, `mktemp`, `printf` — POSIX basics
- `base64` — only needed if you re-run `examples/make_samples.sh`
- `hexdump` — optional alternative to `xxd` shown in the README
There is deliberately no `requirements.txt`/`package.json`: the sample
files are committed to the repository, so nothing needs to be downloaded
or installed. If `xxd` is missing on a minimal Linux container, install it
with your package manager (e.g. `sudo apt install xxd` on Debian/Ubuntu).
starter/byte-detective-worksheet.md (2654 bytes)
# Byte-detective worksheet — Day 005
Answer every question by **running a command and reading real bytes**, not
from memory. The commands you need are named in `starter/inspect_bytes.sh`
and demonstrated in `examples/inspect_bytes.sh`. Work from the lab
directory. Write your answers directly into this file.
## Part 1 — hello.txt (plain ASCII)
Run `xxd examples/samples/hello.txt` and `wc -c examples/samples/hello.txt`.
1. How many bytes is `hello.txt`, and how many characters do you *see* in
the text? Explain why the two numbers relate the way they do (remember
the invisible newline).
Answer:
2. What is the hex value of the first byte, and which character is it?
(Check it against the ASCII column on the right of the xxd output.)
Answer:
3. What is the very last byte of the file in hex, and what character is
it? Why does xxd print a dot for it in the right-hand column?
Answer:
## Part 2 — unicode.txt (UTF-8)
Run `xxd examples/samples/unicode.txt`, then `wc -c` and `wc -m` on it.
4. Which two bytes (hex) encode the character é? How many times does that
two-byte sequence appear in the file, and which words do they belong to?
Answer:
5. `wc -c` and `wc -m` report different numbers for this file. Write both
numbers and account for the difference exactly: which characters take
2, 3, and 4 bytes?
Answer:
6. Which four bytes (hex) encode the party-popper emoji? What do the first
few bits of the leading byte tell a UTF-8 decoder?
Answer:
## Part 3 — tiny.png (binary with magic bytes)
Run `xxd -l 8 examples/samples/tiny.png`, then the full
`xxd examples/samples/tiny.png`, and `file examples/samples/tiny.png`.
7. What are the first 4 bytes of the PNG in hex? Which three ASCII letters
hide inside bytes 2–4?
Answer:
8. `file` reports the image is "2 x 2". Find the readable chunk names in
the full dump (IHDR, tEXt, IDAT, IEND). What human-readable sentence is
stored inside the tEXt chunk?
Answer:
## Part 4 — extensions versus content
Copy the samples under wrong names (use a scratch directory, e.g.
`cp examples/samples/hello.txt /tmp/hello.png`), then run `file` on the
copies.
9. When you rename `hello.txt` to `hello.png`, what does `file` say about
it — and what does that prove about where a file's "type" really lives?
Answer:
10. When you rename `tiny.png` to `tiny.txt` and double-click it or `cat`
it, things look broken — yet `file` still knows what it is. Explain
the difference between what your operating system's file manager uses
(the extension) and what `file` uses (the magic bytes).
Answer:
starter/inspect_bytes.sh (1918 bytes)
#!/usr/bin/env bash
# Day 005 lab — YOUR working file: X-ray the sample files with xxd and file.
#
# The skeleton below runs as-is, but each of the four exercises prints a
# placeholder line. Your job: replace each placeholder `echo` line with the
# exact command named in the comment above it, then re-run the script.
# The finished version is in examples/inspect_bytes.sh — try it yourself
# before peeking.
#
# Run from anywhere: bash starter/inspect_bytes.sh
set -euo pipefail
samples="$(cd "$(dirname "${BASH_SOURCE[0]}")/../examples" && pwd)/samples"
echo "=== Byte detective: my own run ==="
echo
echo "--- Exercise 1: dump plain ASCII text ---"
# Replace the placeholder line below with exactly:
# xxd "${samples}/hello.txt"
# Then answer on the worksheet: which hex byte is the capital H? Which
# byte is the invisible newline at the end?
echo "exercise 1 not completed yet"
echo
echo "--- Exercise 2: find multi-byte UTF-8 characters ---"
# Replace the placeholder line below with exactly:
# xxd "${samples}/unicode.txt"
# Then find the two bytes of e-acute (c3 a9), the three bytes of the euro
# sign (e2 82 ac), and the four bytes of the emoji (f0 9f 8e 89).
echo "exercise 2 not completed yet"
echo
echo "--- Exercise 3: read the PNG magic bytes ---"
# Replace the placeholder line below with exactly:
# xxd -l 8 "${samples}/tiny.png"
# The -l 8 flag limits the dump to the first 8 bytes — the PNG signature.
# On the worksheet, write down the first four bytes in hex.
echo "exercise 3 not completed yet"
echo
echo "--- Exercise 4: prove that extensions lie ---"
# Replace the placeholder line below with exactly:
# cp "${samples}/hello.txt" "${workdir}/hello.png" && file "${workdir}/hello.png"
# The copy has a .png name but text content. Which one does `file` trust?
workdir="$(mktemp -d)"
echo "exercise 4 not completed yet"
rm -rf "${workdir}"
echo
echo "=== End of my run ==="
tests/run_tests.sh (5232 bytes)
#!/usr/bin/env bash
# Tests for the Day 005 lab. Run from the lab directory:
# bash tests/run_tests.sh
#
# Verifies that the committed sample files are what they claim to be
# (checked by content, not by name), that the reference walkthrough runs
# and shows the expected byte sequences, and that the learner's starter
# script at least runs (held to the strict standard once every
# placeholder has been replaced).
set -u
lab_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
samples="${lab_dir}/examples/samples"
failures=0
checks=0
check() {
local label="$1" ok="$2"
checks=$((checks + 1))
if [ "${ok}" = "yes" ]; then
echo " ok: ${label}"
else
echo " FAIL: ${label}"
failures=$((failures + 1))
fi
}
contains() { # contains <haystack> <needle>
case "$1" in *"$2"*) return 0 ;; *) return 1 ;; esac
}
echo "Checking sample files exist ..."
for f in hello.txt unicode.txt tiny.png; do
[ -s "${samples}/${f}" ] && check "samples/${f} exists and is non-empty" "yes" \
|| check "samples/${f} exists and is non-empty" "no"
done
echo "Checking 'file' identifies each sample by content ..."
contains "$(file "${samples}/hello.txt")" "ASCII text" \
&& check "hello.txt is ASCII text" "yes" || check "hello.txt is ASCII text" "no"
contains "$(file "${samples}/unicode.txt")" "UTF-8" \
&& check "unicode.txt is UTF-8 text" "yes" || check "unicode.txt is UTF-8 text" "no"
contains "$(file "${samples}/tiny.png")" "PNG image data" \
&& check "tiny.png is PNG image data" "yes" || check "tiny.png is PNG image data" "no"
echo "Checking the exact bytes with xxd ..."
png_sig="$(xxd -p -l 8 "${samples}/tiny.png" | tr -d '\n')"
[ "${png_sig}" = "89504e470d0a1a0a" ] \
&& check "tiny.png starts with the PNG magic bytes 89 50 4e 47 0d 0a 1a 0a" "yes" \
|| check "tiny.png starts with the PNG magic bytes 89 50 4e 47 0d 0a 1a 0a" "no"
uni_hex="$(xxd -p "${samples}/unicode.txt" | tr -d '\n')"
contains "${uni_hex}" "c3a9" \
&& check "unicode.txt contains the 2-byte sequence c3 a9 (e-acute)" "yes" \
|| check "unicode.txt contains the 2-byte sequence c3 a9 (e-acute)" "no"
contains "${uni_hex}" "e282ac" \
&& check "unicode.txt contains the 3-byte sequence e2 82 ac (euro sign)" "yes" \
|| check "unicode.txt contains the 3-byte sequence e2 82 ac (euro sign)" "no"
contains "${uni_hex}" "f09f8e89" \
&& check "unicode.txt contains the 4-byte sequence f0 9f 8e 89 (emoji)" "yes" \
|| check "unicode.txt contains the 4-byte sequence f0 9f 8e 89 (emoji)" "no"
hello_hex="$(xxd -p "${samples}/hello.txt" | tr -d '\n')"
[ "${hello_hex}" = "48656c6c6f2c20776f726c64210a" ] \
&& check "hello.txt is exactly the 14 ASCII bytes of 'Hello, world!' + newline" "yes" \
|| check "hello.txt is exactly the 14 ASCII bytes of 'Hello, world!' + newline" "no"
echo "Checking content beats extension ..."
tmp="$(mktemp -d)"
cp "${samples}/tiny.png" "${tmp}/disguised.txt"
contains "$(file "${tmp}/disguised.txt")" "PNG image data" \
&& check "'file' still sees PNG content behind a .txt name" "yes" \
|| check "'file' still sees PNG content behind a .txt name" "no"
rm -rf "${tmp}"
echo "Running the reference walkthrough (examples/inspect_bytes.sh) ..."
if output="$(bash "${lab_dir}/examples/inspect_bytes.sh" 2>&1)"; then
check "reference walkthrough exits successfully" "yes"
contains "${output}" "8950 4e47" \
&& check "walkthrough output shows the PNG magic bytes" "yes" \
|| check "walkthrough output shows the PNG magic bytes" "no"
contains "${output}" "c3a9" \
&& check "walkthrough output shows the e-acute bytes" "yes" \
|| check "walkthrough output shows the e-acute bytes" "no"
contains "${output}" "PNG image data" \
&& check "walkthrough shows 'file' identifying the PNG" "yes" \
|| check "walkthrough shows 'file' identifying the PNG" "no"
else
check "reference walkthrough exits successfully" "no"
echo "${output}" | sed 's/^/ /'
fi
echo "Running your starter script (starter/inspect_bytes.sh) ..."
if s_output="$(bash "${lab_dir}/starter/inspect_bytes.sh" 2>&1)"; then
check "starter script exits successfully" "yes"
else
check "starter script exits successfully" "no"
echo "${s_output}" | sed 's/^/ /'
s_output=""
fi
if contains "$(cat "${lab_dir}/starter/inspect_bytes.sh")" "not completed yet"; then
echo "Note: starter still has unfinished exercises — structure checked only."
contains "${s_output}" "=== Byte detective: my own run ===" \
&& check "starter prints its header" "yes" || check "starter prints its header" "no"
else
contains "${s_output}" "8950 4e47" \
&& check "your run shows the PNG magic bytes (exercise 3)" "yes" \
|| check "your run shows the PNG magic bytes (exercise 3)" "no"
contains "${s_output}" "c3a9" \
&& check "your run shows the e-acute bytes (exercise 2)" "yes" \
|| check "your run shows the e-acute bytes (exercise 2)" "no"
contains "${s_output}" "ASCII text" \
&& check "your run shows 'file' trusting content over the .png name (exercise 4)" "yes" \
|| check "your run shows 'file' trusting content over the .png name (exercise 4)" "no"
fi
echo
echo "${checks} checks, ${failures} failure(s)."
[ "${failures}" -eq 0 ]
Troubleshooting
Troubleshooting — Day 005 lab
command not found: xxd
xxd ships with vim and is preinstalled on macOS. On a minimal Linux
system install it (sudo apt install xxd on Debian/Ubuntu, sudo dnf install vim-common on Fedora), or use hexdump -C instead — its output
is equivalent for this lab (bytes on the left, ASCII on the right), only
formatted slightly differently.
wc -m prints 32 instead of 24 for unicode.txt
Character counting depends on your locale. In a C/POSIX locale, wc -m
counts bytes. Check with locale; if LC_CTYPE is not a UTF-8 locale,
run the command as LC_ALL=en_US.UTF-8 wc -m examples/samples/unicode.txt
(any UTF-8 locale works). The byte count from wc -c is locale-independent.
The emoji or accents display as boxes or question marks
That is a font/terminal limitation, not a data problem — the bytes in
the file are correct (verify with xxd: the sequence f0 9f 8e 89 is
there). Try a different terminal profile or font. This is a live
demonstration of the lesson's point that rendering and encoding are
separate layers.
file output wording differs from the expected output
Different file versions phrase results differently (UTF-8 text versus
Unicode text, UTF-8 text; extra notes like with no line terminators).
The tests only look for the substrings ASCII text, UTF-8, and
PNG image data, which every current version prints.
Tests fail with tiny.png starts with the PNG magic bytes … FAIL
The sample was probably corrupted by an editor or a checkout that
translated line endings (byte 0a → 0d 0a breaks binary files — that is
exactly why the PNG signature contains both). Regenerate all three samples:
bash examples/make_samples.sh, then re-run the tests.
base64: invalid option -- d when running make_samples.sh
Older macOS base64 uses -D (capital) to decode; the script tries -d
first and falls back automatically. If you see this message but the script
still ends with three file lines, everything worked.
My own text file shows ef bb bf at the start
That is a UTF-8 byte-order mark (BOM) — three bytes some Windows editors prepend. It is legal but often unwanted; many Unix tools choke on it. Save without BOM, or note it as a finding: you just diagnosed a real encoding issue from raw bytes.
Windows: bash is not recognized
Use WSL (wsl --install, then open Ubuntu and work from there — xxd and
file are available). PowerShell's Format-Hex can substitute for xxd
if you prefer to explore manually, but the scripts and tests assume a
Unix-style shell.
Security notes
Security notes — Day 005 lab
- What the scripts do: read the three committed sample files with
file,wc, andxxd, copy two of them into a throwaway temp directory (removed at the end of the run), and print text. They make no network connections, modify no files outsidemktemp -dscratch space, and change no settings. - Privileges: everything runs as your normal user; nothing needs
sudo. - Why this lab is itself a security lesson: file extensions are a
naming convention, not a property of the data — the rename experiment
proves a
.txtcan be a PNG and vice versa. Attackers exploit exactly this gap (a "document" that is really an executable, an upload that lies about its type). Tools that check magic bytes, likefile, judge content instead of names. Inspecting bytes withxxdis read-only and always safe; opening an unknown file with an application is what carries risk, because parsers of complex formats have historically been a rich source of vulnerabilities. - Privacy: the committed samples contain no personal data, and the walkthrough prints bare file names, not your local paths. If you inspect your own files while experimenting, remember a hex dump reveals everything in the file — including metadata you may not see in a normal viewer — so think before pasting dumps of personal files into a class forum.