Python › Module 1 › Lesson 3
File Handling
Read and write log files and wordlists safely with Python’s open(), paths, and context managers
Opening
Your first sensor is often a text file. open() without a path policy is how you read the wrong universe.
Auth logs, web access logs, DNS exports — defenders live in files. Lesson 2 looped a tiny list in memory. Real hunts iterate lines on disk: for line in f, if needle in line. Python’s with open(...) as f: is the context manager that opens, yields the handle, and closes it even if your loop raises. pathlib.Path is how you build the path without slash-guessing. encoding="utf-8" is how you admit logs are text with a contract, not a pile of mystery bytes. Safety is the lesson, not just syntax. You will read and write only under $HOME/cyberlium-lab on a machine you own. You will never open /etc/shadow, /etc/passwd “for the hashes,” another user’s mailbox, or a laptop you borrowed without a written lab on files they gave you. A “wordlist” here is a file of sample strings you wrote so you can practice parsing and substring checks — not a weapon against someone else’s login. The next lesson’s lab uses a fictional sample_auth.log you create. This lesson teaches the handles you will use there.
1. with open() as f: the context manager is the close button you cannot forget
open(path, mode, encoding=...) returns a file object. Mode "r" reads (file must exist). "w" writes, creating the file or truncating it to empty — that is a destroy-and-replace, not an append. "a" appends. "x" fails if the file exists (useful when you do not want to clobber). Text mode is the default; binary "rb"/"wb" is for hashes of raw bytes (Lesson 1 used Path.read_bytes()). If you write f = open(...) and then return or raise, the handle can leak until the process ends. with open(...) as f: enters a context: the block runs, then f.close() is called on the way out, exception or not. That is the mechanism. It is not slower in any way you should care about. It is how scripts survive a mid-loop UnicodeError without leaving a lock on Windows.
Iterate with for line in f: so you stream. f.read() slurps the whole file; fine for a 20-line lab log, painful and memory-heavy for a multi-gigabyte export. line still includes the newline; line.strip() is the usual hunt form. "needle in line" is a substring check — the DNA of the next lab — not a regex yet (regex is a later module). Write with out.write(item + " ") or print(item, file=out). Always pass encoding="utf-8" for lab text so Windows and Unix agree. errors="ignore" or "replace" is a conscious choice for messy logs; silent ignore can hide the line you needed. In this course, prefer utf-8 and fix the sample file you authored if it is broken.
2. pathlib.Path: join without string glue, and stay inside the lab tree
Path.home() / "cyberlium-lab" / "practice.log" builds a path object. / here is Path’s join operator, not a root you are escaping to. str(path) if an API wants a string. path.write_text(...) and path.read_text(encoding=) are short forms that still need an encoding. path.open("r", encoding="utf-8") works inside with. The security habit is a root variable: lab = Path.home() / "cyberlium-lab"; then only lab / filename. If you later take a filename from a user, a real tool must reject .. and absolute paths so "wordlist" cannot become /etc/shadow (path traversal). This beginner course does not take untrusted pathnames. You hardcode the lab root. That is a feature.
Never Path("/etc/shadow"), Path("/etc/passwd"), or a classmate’s absolute Downloads path “because the log is there.” Never open a file over a share you do not own. If mkdir fails, create $HOME/cyberlium-lab yourself; do not fall back to C:\ or /. Relative open("auth.log") depends on the current working directory — a classic “it worked in my IDE” bug that reads a different file when you run from another folder. Prefer Path.home() so the location does not depend on pwd. Then chmod 600 files that might hold findings, even fictional ones, so a shared account on the same PC is not your SIEM.
3. Wordlists as parsing practice — not login ammunition
Security tutorials love “wordlists”: one string per line. In attack culture that file is a password dictionary aimed at a login. In this course a wordlist is data you wrote — needles, usernames from a fictional lab, sample status tokens — so you can practice for line in f and membership tests. Creating rockyou.txt clones to spray at a website is not File Handling homework. Reading a wordlist you authored into a list, writing a filtered copy, and chmod 600 the result is File Handling homework. If a blog says otherwise, it is not this syllabus. The file extension does not change the ethics: .txt in cyberlium-lab is yours; a dump from a breach is not a lab asset.
4. Encoding, errors, and why “just open it” fails on real logs
Text in Python 3 is str (Unicode). Bytes on disk need a codec. utf-8 is the lab default. Some Windows logs are utf-16; some appliances emit latin-1. If you omit encoding, the default can differ by platform and your script becomes a lottery. UnicodeDecodeError is a gift: it tells you the contract failed. Swallowing it with a bare except is how you skip the line that had the attacker’s IP as mojibake. For files you create, write utf-8 and read utf-8. For a future export you are allowed to have, document the encoding in the JSON note beside it. Binary hashing stays on bytes (Lesson 1). Do not hash a decoded str and think it matches another tool’s file digest.
5. Wrong vs right: /etc/shadow and other people’s files vs $HOME/cyberlium-lab
Worked failure — “the interesting logs are in /etc.” Right is with open + pathlib inside a lab root you own, chmod 600.
Wrong
open("/etc/shadow"), open("/etc/passwd"), or a partner’s auth.log without permission “to learn hashlib.” f = open(path) with no with, then crash and leave the file locked. open("report.txt", "w") from a random cwd and overwrite something else. Download a breach wordlist and aim it at a login. Skip encoding. chmod 644 on a file that later gets API keys. Path("../" * 8 + "etc/shadow") as a joke. This course forbids that.
Right
lab = Path.home() / "cyberlium-lab". with open(lab / "practice.log", "r", encoding="utf-8") as f: for line in f. Write reports next to the source. Wordlists are sample lines you authored for parsing. chmod 600. Never other people’s secret files. Next — Lab — First Python Script — fictional sample_auth.log and a script that prints matching Failed password lines.
6. Practical: write a tiny log, read it back, write a filtered report
Create practice.log with a few fictional lines you type. Read with a context manager. Write hits.txt with only the lines that contain a needle you chose (for example Failed password). That is the entire next lab, minus the story dressing. Keep both files in the lab directory. chmod 600. Do not copy a real production log from work into the folder — work logs can hold customer data you are not allowed to take home.
Command guide
file_lab.py — with open, pathlib, utf-8; lab tree only
DEFENSIVE file lab. YOUR machine. $HOME/cyberlium-lab only. NEVER open /etc/shadow, /etc/passwd, or anyone else's files. Wordlists here are sample strings YOU wrote — not login attack ammo.
Command — copy this
mkdir -p "$HOME/cyberlium-lab" cd "$HOME/cyberlium-lab"
Command — copy this
cat > file_lab.py << 'EOF'
from pathlib import Path
lab = Path.home() / "cyberlium-lab"
lab.mkdir(parents=True, exist_ok=True)
log_path = lab / "practice.log"
report_path = lab / "hits.txt"
wordlist_path = lab / "sample_needles.txt"
# Files we authored. Not /etc/shadow. Not a breach dump.
log_path.write_text(
"sshd: Failed password for root from 10.0.0.5 port 51234 (FICTIONAL)
"
"sshd: Accepted password for alice from 10.0.0.9 port 51111 (FICTIONAL)
"
"sshd: Failed password for admin from 10.0.0.5 port 51235 (FICTIONAL)
",
encoding="utf-8",
)
wordlist_path.write_text("Failed password
Accepted password
", encoding="utf-8")
needle = "Failed password"
hits = []
with log_path.open("r", encoding="utf-8") as f:
for line in f:
if needle in line:
hits.append(line.strip())
with report_path.open("w", encoding="utf-8") as out:
for item in hits:
out.write(item + "
")
print(f"wrote {len(hits)} hit(s) to {report_path}")
# NEVER: Path("/etc/shadow").read_text()
# NEVER: use sample_needles.txt against a live login
EOFCommand — copy this
python file_lab.py || python3 file_lab.py
chmod 600 "$HOME/cyberlium-lab/practice.log" \
"$HOME/cyberlium-lab/hits.txt" \
"$HOME/cyberlium-lab/sample_needles.txt" \
"$HOME/cyberlium-lab/file_lab.py"Windows without chmod: WSL/Git Bash, or restrict the files in your profile.
NEVER: open other people's mail, shadow, or work production logs NEVER: brute-force logins with a wordlist NEVER: store live passwords in these files
Mission: with open + pathlib in cyberlium-lab (mode 600)
1) Explain why with open(...) as f: closes the file even if the loop raises, and why encoding="utf-8" belongs on lab text. 2) Using pathlib, read only under $HOME/cyberlium-lab; write a filtered hits file. Do not open /etc/shadow or anyone else’s files. 3) Treat any wordlist as parsing practice you authored, not attack ammo. chmod 600 the log, report, and script.
Stuck? Ask Cyberlium AI Mentor
If “the real logs are in /etc so the lab should open them” still feels true, ask for a hint — not a shadow path. Try: "Hint only: why does with open close the handle, why Path.home() / cyberlium-lab beats a relative auth.log, and why is a wordlist in this course not a login weapon?" You still write file_lab.py. No other people’s files. No live passwords in the report.
You can stream lines with a context manager, join paths with pathlib from a lab root, declare utf-8, and refuse system secret files and other people’s data. A wordlist is practice text you wrote. Next — Lab — First Python Script — you will create a fictional sample_auth.log, chmod 600 it, and print every line that contains Failed password: the same mechanism, now as a hunter you run on purpose.
Knowledge Check
APPLY: You need failed-password lines from a log you created. A classmate uses f = open("/etc/shadow") with no with, no encoding, cwd-relative names. What is the defensive pattern?
Multiple choice
Knowledge Check
APPLY: You write sample_needles.txt with one phrase per line to practice if needle in line. Correct use, and what is open(path, "w") doing to an existing file?
Multiple choice
Knowledge Check
APPLY: True or False: Security scripts often read logs line by line with for line in f, and $HOME/cyberlium-lab files that might hold findings should be chmod 600 — still never other people’s secret files.
True or False