Home / Learn Linux / Project 1
Clean up a messy folder
Sort hundreds of files by type and age, search logs, archive the old ones and link the latest.
- Project
- 1 of 5
- Time
- About 60 minutes
- Uses
- Lessons 4 to 12
On this page
- What you'll build
- Step 0: make the mess
- Step 1: survey it
- Step 2: make the new layout
- Step 3: delete the junk, safely
- Step 4: sort by type
- Step 5: find the files with spaces
- Step 6: what's wrong with the servers?
- Step 7: archive the old files
- Step 8: point latest at the newest data
- Step 9: fix the config
- Step 10: the final check
- Take it further
What you'll build
Everyone has a folder like this: a downloads directory with hundreds of files, photos mixed with invoices and logs, names with spaces, empty leftovers and files nobody has opened in a year. You'll clean it up using only what you learned in lessons 4 to 12:
- sort every file into a directory by type
- delete the junk safely
- pull a report of the errors hidden in the server logs
- archive and remove anything older than 90 days
- point a
latestlink at the newest data file - fix a config file with
sed
Each step says what to do, gives you a hint if you need one, shows what you should see when it worked, and hides a full solution. Try each step on your own first.
Step 0: make the mess
Paste this whole block into your VM. It builds ~/project1/downloads with 113 files. Don't worry about understanding it all; it's just setup:
$ mkdir -p ~/project1/downloads && cd ~/project1/downloads $ touch IMG_{1001..1040}.jpg holiday-{1..12}.JPG screenshot-{1..15}.png $ touch invoice-2025-{01..12}.pdf invoice-2026-{01..09}.pdf $ touch notes-{1..10}.txt "meeting notes.txt" "final final v2.txt" $ seq 1 500 > sales-2026-08.csv; seq 1 800 > sales-2026-09.csv; seq 1 650 > sales-2026-10.csv $ printf "INFO start\nERROR db timeout\nINFO ok\nWARN slow query\nERROR db timeout\nERROR disk full\n" | tee server-1.log server-2.log > /dev/null $ printf "INFO start\nERROR auth failed\nERROR db timeout\nWARN slow query\n" > server-3.log $ printf "app_name = shop\ndata_dir = /home/olduser/data\nlog_dir = /home/olduser/logs\nport = 8080\n" > app.conf $ touch empty-{1..6}.tmp $ touch -d "200 days ago" IMG_10{01..20}.jpg invoice-2025-*.pdf $ touch -d "120 days ago" notes-{1..5}.txt sales-2026-08.csv $ touch -d "3 days ago" sales-2026-09.csv $ ls | wc -l 113
Step 1: survey it
Before changing anything, find out what's there. Answer these three questions with one command each:
- How many files are there?
- How much space do they take?
- How many files of each extension are there, most common first?
Hint
For 3, sed can strip everything up to the last dot (s/.*\.//), then sort | uniq -c | sort -rn counts and ranks, just like the page counts in lesson 8.
Solution
$ find . -type f | wc -l 113 $ du -sh . 32K . $ ls | sed 's/.*\.//' | sort | uniq -c | sort -rn 40 jpg 21 pdf 15 png 12 txt 12 JPG 6 tmp 3 log 3 csv 1 conf
Notice JPG and jpg are counted separately. Linux is case-sensitive, so you'll need -iname when you sort the photos.
Step 2: make the new layout
Inside ~/project1, create a sorted directory with images, documents, data, logs, config and other inside it, in one command.
Solution
$ mkdir -p sorted/{images,documents,data,logs,config,other} $ tree sorted sorted ├── config ├── data ├── documents ├── images ├── logs └── other 7 directories, 0 files
Step 3: delete the junk, safely
Some files are empty leftovers. Find every empty file in downloads, look at the list, then delete exactly those.
Hint
find has -empty. Run it without -delete first. Careful: the photos and invoices you made with touch are empty too, so limit the search to the .tmp files.
Solution
$ find . -type f -name "*.tmp" -empty ./empty-1.tmp ./empty-2.tmp ./empty-3.tmp ./empty-4.tmp ./empty-5.tmp ./empty-6.tmp $ find . -type f -name "*.tmp" -empty -delete $ ls *.tmp ls: cannot access '*.tmp': No such file or directory
This step is why you always look before you delete. find . -empty -delete on its own would have wiped every photo and invoice in this practice folder, because touch made them all empty.
Step 4: sort by type
Move the files into place:
.jpg,.JPGand.pngintosorted/images.pdfand.txtintosorted/documents.csvintosorted/data.logintosorted/logs.confintosorted/config
Hint
find with \( -iname "*.jpg" -o -iname "*.png" \) matches all the photos. GNU mv has -t dir, which puts the destination first, so -exec mv -t ../sorted/images {} + moves them in one go. Names with spaces are safe this way, because find passes each name as one argument.
Solution
$ find . -type f \( -iname "*.jpg" -o -iname "*.png" \) -exec mv -t ../sorted/images {} + $ find . -type f \( -name "*.pdf" -o -name "*.txt" \) -exec mv -t ../sorted/documents {} + $ mv *.csv ../sorted/data/ $ mv *.log ../sorted/logs/ $ mv *.conf ../sorted/config/ $ ls | wc -l 0
downloads is empty now. Check where everything went:
$ for d in sorted/*; do echo "$d $(ls "$d" | wc -l)"; done
sorted/config 1
sorted/data 3
sorted/documents 33
sorted/images 67
sorted/logs 3
sorted/other 0That for loop counts each directory. Lesson 28 explains loops; for now, copy it as it is.
Step 5: find the files with spaces
Two documents have spaces in their names, which is annoying on a server. Find them, then rename them to use dashes instead.
Hint
find with -name "* *". To rename one, quote the old name: mv "meeting notes.txt" meeting-notes.txt.
Solution
$ find . -name "* *" ./meeting notes.txt ./final final v2.txt $ mv "meeting notes.txt" meeting-notes.txt $ mv "final final v2.txt" final-v2.txt $ find . -name "* *" | wc -l 0
Step 6: what's wrong with the servers?
The three logs in sorted/logs hold the real story. Produce a report:
- How many
ERRORlines does each log have? - Across all logs, which error messages happen most often? Show each message once, with its count, most frequent first.
- Save the answer to 2 in
~/project1/error-report.txtand show it at the same time.
Hint
grep -c counts per file. For 2, grep -h leaves out the file names, sed can strip the ERROR prefix, and sort | uniq -c | sort -rn ranks them. tee saves and shows.
Solution
$ grep -c ERROR *.log server-1.log:3 server-2.log:3 server-3.log:2 $ grep -h ERROR *.log | sed 's/^ERROR //' | sort | uniq -c | sort -rn | tee ~/project1/error-report.txt 5 db timeout 2 disk full 1 auth failed
db timeout is the problem: five times across three servers.
Step 7: archive the old files
Anything not modified in the last 90 days should be packed into one archive and removed from sorted.
- List every file under
sortedolder than 90 days and save the list toold-files.txt. - Make
old-YYYY-MM-DD.tar.gz(today's date) from that list, removing the files as they're added. - Check the archive's contents and how many files are left in
sorted.
Hint
find sorted -type f -mtime +90. GNU tar reads names from a file with -T old-files.txt, and --remove-files deletes each file after it's safely in the archive.
Solution
$ find sorted -type f -mtime +90 > old-files.txt $ wc -l < old-files.txt 38 $ tar -czf old-$(date +%F).tar.gz --remove-files -T old-files.txt $ tar -tzf old-*.tar.gz | wc -l 38 $ find sorted -type f | wc -l 69
Step 8: point latest at the newest data
Make a symlink ~/project1/latest.csv that always points at the newest file in sorted/data. Then check where it points.
Hint
ls -t lists newest first, so ls -t sorted/data/*.csv | head -n 1 is the newest. Put that inside $(...) in an ln -sfn command.
Solution
$ ls -lt sorted/data total 8 -rw-rw-r-- 1 ubuntu ubuntu 2492 Oct 21 11:37 sales-2026-10.csv -rw-rw-r-- 1 ubuntu ubuntu 3092 Oct 18 11:37 sales-2026-09.csv $ ln -sfn "$(ls -t sorted/data/*.csv | head -n 1)" latest.csv $ ls -l latest.csv lrwxrwxrwx 1 ubuntu ubuntu 29 Oct 21 11:37 latest.csv -> sorted/data/sales-2026-10.csv $ wc -l latest.csv 650 latest.csv
Careful: the newest modified file isn't always the one with the latest date in its name. Here sales-2026-10.csv was created last, so it wins.
Step 9: fix the config
sorted/config/app.conf still points at a user who doesn't exist any more. Change every /home/olduser to /home/ubuntu, and change the port to 9000, keeping a backup. Show the result.
Hint
Paths are full of slashes, so use another separator: s|old|new|g. Two changes, one sed with -e twice.
Solution
$ sed -i.bak -e 's|/home/olduser|/home/ubuntu|g' -e 's/^port = .*/port = 9000/' app.conf $ cat app.conf app_name = shop data_dir = /home/ubuntu/data log_dir = /home/ubuntu/logs port = 9000 $ ls app.conf app.conf.bak
Step 10: the final check
Show the finished layout, and remove the empty downloads directory without risking anything that's still inside it.
Solution
$ rmdir downloads $ tree -L 3 --filelimit 15 . ├── error-report.txt ├── latest.csv -> sorted/data/sales-2026-10.csv ├── old-2026-10-21.tar.gz ├── old-files.txt └── sorted ├── config │ ├── app.conf │ └── app.conf.bak ├── data │ ├── sales-2026-09.csv │ └── sales-2026-10.csv ├── documents [16 entries exceeds filelimit, not opening dir] ├── images [47 entries exceeds filelimit, not opening dir] ├── logs │ ├── server-1.log │ ├── server-2.log │ └── server-3.log └── other 8 directories, 11 files
rmdir would have refused if anything were left. --filelimit 15 stops tree from listing directories with more than 15 entries and shows how many there are instead, which keeps the big images and documents directories from flooding the screen.
Take it further
- Sort the photos into one directory per month using their modify dates. (
find -newermttakes a date, anddate -r file +%Y-%mprints a file's month.) - Write the whole cleanup as a script you can run on any folder. Come back to this after the Bash lessons (27 to 29); it's a good first script.
- Schedule the archiving step to run every night with cron (lesson 24).
← Editing text with sed and regular expressionsUsers and groups →