Linux Lab: Advanced File Operations (find · grep · sed · links)
Environment: Debian 13 (any recent Debian/Ubuntu works) · Time: approx. 3–4 lessons
Submission: a text file answers.md containing, for every task, the command(s) you used and, where asked, a short answer in your own words.
Goals
After this lab you can
- search for files by name, regular expression, size, age, type and permissions with
find - search inside files with
grepand extended regular expressions - combine tools safely with pipes,
xargsandfind -exec - edit files non-interactively with
sed(substitution, deletion, capture groups, in-place editing) - explain and use hard links and symbolic links and recognise their pitfalls
Rules of the game
You will not find every option on this sheet. That is intentional. Use the documentation:
| Where to look | How |
|---|---|
| Manual pages | man find, man grep, man sed, man ln, man 7 regex |
| Search inside a man page | type / followed by a word, e.g. /-regextype, then n for the next hit |
| Find the right man page | apropos link or man -k link |
| Longer documentation (sed!) | info sed – or the GNU manual online |
| Quick reminder | find --help, grep --help, sed --help |
💡 Tip: Try every command without changing anything first. With
sed, that means: run it without-iand look at the output before you write to the file.
Part 0 – Setup
Use the setup script from linux-file-tools_assignment_setup, save it in your home directory and run it:
chmod +x setup-linux-lab.sh
./setup-linux-lab.sh
cd ~/linux-labThe script creates the directory ~/linux-lab with logs, config files, small projects, notes and media files. Running it again resets everything – so if you break something, just start over.
0.1 Get an overview. Display the full directory tree of ~/linux-lab including hidden files.
Hint: tree may not be installed (sudo apt install tree). Can you also do it with find alone?
Part 1 – Finding files with find
General form: find [where] [tests] [actions]. Work from inside ~/linux-lab.
1.1 Find all files ending in .log.
1.2 Find all JPEG images – regardless of upper/lower case (.jpg, .JPG, .jpeg, …).
Hint: there is a case-insensitive variant of -name.
1.3 In notes/ there are files note_01.txt, note_2.txt, note_10.txt, … Find only the notes with exactly two digits in their number, using a regular expression.
Hints:
- Look up
-regexand-regextypeinman find. Use theposix-extendedtype. - Important:
-regexmatches the whole path (e.g../notes/note_01.txt), not only the file name!
1.4 Using a single -iregex, find all image files ending in .jpg, .jpeg or .png (any case).
Hint: alternatives (a|b), optional characters e?.
1.5 Find all regular files larger than 1 MiB and display them with ls -lh (using a find action, not a pipe).
Hint: -size and -exec … \;
1.6 Find all empty files.
1.7 Find all hidden files (not directories).
1.8 Find
a) files with permissions exactly 777,
b) files the owner may execute (any other bits may be set too).
What is the difference between -perm 755, -perm -755 and -perm /111?
1.9 Find all files that were last modified before 1 January 2026.
Hint: -newermt and negation ! (or -not).
1.10 In logs/archive/, find all rotated log files (app-<date>.log) modified after 10 September 2026.
1.11 Find all files whose name contains a space. Why are such files dangerous in scripts?
1.12 – Short answer: find . -size -1M only returns empty files, although song.mp3 (1.5 MiB) and many small files exist. Find out why (man find, section about -size). How do you correctly find files smaller than 1 MiB?
Part 2 – Searching file contents with grep
Useful options to look up: -r, -i, -n, -l, -c, -v, -w, -o, -h, -E, --include.
2.1 Show all lines in logs/ containing ERROR, including the line number.
2.2 The developers were inconsistent: some errors are logged as error. Show all error lines in logs/app.log case-insensitively, but only where error is a whole word.
2.3 List only the names of all files below ~/linux-lab that contain TODO.
2.4 Extract all e-mail addresses from all files, print each address only once, sorted.
Hints: -o prints only the matching part, -h suppresses file names, sort -u removes duplicates. Build the regex step by step: something@something.at least two letters.
2.5 From logs/access.log, extract the client IP addresses (start of each line) and count how often each one occurs. Sort by frequency, highest first.
Hint: uniq -c, sort -n, sort -r
2.6 – Bonus: One of the IPs in access.log is invalid. Write a stricter regex that accepts only octets from 0 to 255.
2.7 Show config/config.ini without comment lines (starting with #) and without empty lines.
Hint: invert the match with -v; ^ and $ anchor the beginning and end of a line.
2.8 Find all lines in notes/phone.txt that contain a phone number (Austrian format: either starting with +43 or with 0).
2.9 Find all requests in logs/access.log that ended with a server error (HTTP status 5xx).
2.10 Find all dates in the format DD.MM.YYYY in the projects/ folder.
Hint: \b marks a word boundary; a literal dot must be escaped.
Part 3 – Combining tools
3.1 First, run the following command and explain the error messages:
find . -name '*.md' | xargs grep -l 'Published'Then fix it so it also works with file names containing spaces.
Hint: look for -print0 in man find and -0 in man xargs.
3.2 Find all files ending in .conf or .ini and show every line in them containing debug (with file name and line number).
Hint: -o means OR in find. Look up how to group expressions with \( … \) – and why you need it as soon as you add -type f.
3.3 Solve the TODO search from 2.3 once more, this time only in projects/, searching for TODO or FIXME, using find … -exec grep … {} +.
What is the difference between -exec … {} \; and -exec … {} +?
3.4 Display the three largest files in the lab with their size in bytes, largest first.
Hint: find -printf (look up %s and %p), then sort and head.
Part 4 – Editing files with sed
⚠️ From now on, files will be changed. Always test without
-ifirst!
4.1 Output config/server.conf with debug = true replaced by debug = false – without changing the file.
4.2 Now change debug=true to debug=false in config/config.ini in place, and have sed create a backup config.ini.bak at the same time.
Hint: -i accepts a suffix. Verify both files afterwards (diff is your friend).
4.3 Print only lines 3 to 5 of logs/app.log using sed.
Hint: -n and the p command.
4.4 Output config/config.ini without comments and empty lines – this time with sed (compare with 2.7).
Hint: the d command, several commands are separated by ;.
4.5 Print only the [database] section of config/config.ini (from the header up to the next empty line or end of file).
Hint: addresses can be ranges of two regexes: /start/,/end/.
4.6 The blog posts use the German date format DD.MM.YYYY. Output projects/blog/post2.md with all dates converted to ISO format YYYY-MM-DD.
Hints: -E for extended regex, capture groups ( … ), back-references \1, \2, \3.
4.7 All links in the blog posts still use http://. Change them to https:// in all .md files in projects/blog/ in place.
Hint: slashes in the pattern get ugly – sed lets you choose another delimiter, e.g. s|…|…|.
4.8 The copyright year in the webshop is outdated. Use find together with sed -i to replace 2025 by 2026 in all .html files below projects/.
4.9 Before you show config/server.conf on the projector, mask the password: the output should read db_password = ********. Keep the original part db_password = using a capture group. Do not change the file.
4.10 In config/config.ini, the option after the line # legacy option should be commented out (prefix # ) – in place. You may not use line numbers, because the file could change.
Hint: look up the n command and command blocks { … } in info sed.
4.11 Using sed, output notes/shopping.txt
a) with a new first line # generated file,
b) with a new last line tea.
Hint: commands i and a, addresses 1 and $.
4.12 – Bonus: From logs/access.log, print for every request just the date and the status code, e.g. 01/Oct/2026 200.
Part 5 – Links
Before you start, read the DESCRIPTION in man ln and find out what an inode is (man 7 inode, or the web). Useful tools: ls -li, stat, readlink.
Hard links
5.1 Create a hard link backup/shopping_hard.txt pointing to notes/shopping.txt. Display the inode number and the link count of both files. What do you notice?
5.2 Append the line eggs to backup/shopping_hard.txt. Look at notes/shopping.txt. Explain.
5.3 Delete notes/shopping.txt. Is the content gone? What is the link count of backup/shopping_hard.txt now? When does Linux actually free the data blocks?
Afterwards, restore notes/shopping.txt as a hard link again.
5.4 Try to create a hard link to the directory notes/. What happens and why does Linux forbid it?
5.5 Try to create a hard link from notes/phone.txt to /dev/shm/phone_hard. Read the error message and explain it. (Hint: df -T /dev/shm ~)
Symbolic links
5.6 In backup/, create a symbolic link server.conf.lnk pointing to config/server.conf using a relative path. Display the link target with ls -l and with readlink. What does readlink -f do?
Hint: a relative link target is relative to the directory of the link, not to your current directory!
5.7 Create two symbolic links to logs/app.log in backup/:
applog_abswith an absolute target,applog_relwith a relative target.
Then rename the whole lab folder (mv ~/linux-lab ~/linux-lab-moved) and try to cat both links. Explain the result. Rename the folder back afterwards.
5.8 Create a link backup/broken pointing to ../notes/does_not_exist.txt. Now find all broken symbolic links in the lab with find.
Hint: compare -type l with -xtype l in man find.
5.9 Create a symbolic link backup/notes_link to the notes directory. Does it work (unlike 5.4)? List the directory through the link.
5.10 Change the existing link backup/applog_rel so it points to ../logs/access.log – with a single ln command, without rm first.
Hint: -f and -n in man ln.
Finding links
5.11 Find all names (hard links) that refer to the same file as notes/shopping.txt. Solve it in two ways: with -samefile and with -inum.
5.12 Find all regular files in the lab that have more than one hard link.
Summary
5.13 Complete the table in your answers.md:
| Property | Hard link | Symbolic link |
|---|---|---|
| Has its own inode? | ||
| Works across file systems? | ||
| Can point to a directory? | ||
| What happens if the original is deleted? | ||
| What happens if the original is moved/renamed? | ||
Visible in ls -l how? |
Part 6 – Bonus: putting it all together
6.1 Rename all .JPG and .jpeg files in media/photos/ so they end in .jpg.
Hint: a for loop and bash parameter expansion ${f%.*} – or look at the rename package.
6.2 Write a one-liner that finds all .log files below logs/ (not in archive/), counts the ERROR lines per file (case-insensitive) and prints only files with at least one error.
Hint: -path … -prune, or -not -path …; grep -c.
6.3 Explain in your own words what this command does – before you run it:
find . -type f -name '*.bak' -mtime -1 -exec sed -n '1p' {} \; -deleteWas -delete placed in the right position? What would happen if it were placed directly after find .?