Step 10. ★ Project: Automatic File Classification — A Robot That Cleans Your Downloads Folder
Level 0 — Understanding Computer Operation and Structure | Difficulty ★★★☆☆ | Estimated time 3–4 hours
Prerequisite: Step 9 complete. We will work in Windows PowerShell. This is the first comprehensive project drawing on every skill from Steps 7–9.
- What you need: a Windows PC, PowerShell, Notepad, and a folder containing a few files to organize (your Downloads folder or a practice folder).
- Safety: a script that moves files, written carelessly, can send files to the wrong place. So we first learn a safety device called
-WhatIf(preview), and only then run for real. That order is today’s core habit. - Note: this is a ★ project chapter. The emphasis is on "assembling what you’ve learned into a working product" rather than new concepts. Remember to save with encoding "UTF-8 with BOM," as you learned in Step 9.
What does your Downloads folder look like right now? Installers, photos, PDFs, and zip files all jumbled together, forcing you to scroll up and down to find anything… On most computers, the Downloads folder becomes a "digital warehouse." Today you will build an automatic classifier: run one script, and the files in a folder get sorted into subfolders by extension. When you finish, you’ll experience the first moment where "learning" becomes a "tool."
1. Learning Objectives
By the end of this chapter, you will be able to:
- Design the algorithm (processing order) in plain language before writing code
- Write code that works regardless of the username, using environment variables (
$env:USERPROFILE) - Preview the result of a dangerous command with
-WhatIfbefore executing it - Handle edge cases in code, such as files without extensions and name collisions
- Assemble the skills from Steps 7–9 into a working auto-classification script
2. Background Knowledge — Today’s Tools and Concepts
Today’s Tools at a Glance
| Category | Details |
|---|---|
| Language & environment | PowerShell 5.1 script (.ps1), targeting the Windows file system |
| Today’s commands | New-Item -ItemType Directory (create folder), Move-Item (move file), -WhatIf (preview), Test-Path (existence check) |
| Review commands | Get-ChildItem -File (Step 2), foreach & if (Step 8), variables & environment variables (Step 7) |
| Concepts needed | Algorithm (designing the processing order), edge cases, idempotency |
Most of the technology going into this project is already in your toolbox — variables (Step 7) + getting a file list (Step 2) + loops and conditionals (Step 8) + creating folders and moving files (Step 2) + running scripts (Step 9). The only new commands are the four in the table above. Everything else is review and assembly.
2-1. Think Before You Code — The Algorithm
In a project chapter, we do the design before the code. Writing down "what to do, in what order" in plain language — this is called an algorithm. It sounds grand, but it’s the same kind of work as writing a "cooking order" or an "assembly manual."
Our classifier’s algorithm:
1. 대상 폴더의 파일 목록을 얻는다
2. 파일마다 반복한다:
2-1. 이 파일의 확장자를 확인한다 (.pdf, .jpg...)
2-2. 그 이름의 폴더가 없으면 만든다
2-3. 파일을 그 폴더로 옮긴다
3. 결과를 확인한다
Once this sequence is vivid in your mind, the code flows out easily. The difference between a beginner and an expert isn’t typing speed — it’s whether you draw this picture first.
2-2. -WhatIf — Today’s Star
-WhatIf is a switch you attach to commands like Move-Item; it moves nothing at all and only shows "what would have happened if executed." The habit of rehearsing before dangerous commands (move, delete, modify) — this is the safety instinct that separates beginners from professionals.
2-3. Why a Real Folder, of All Things
There’s a reason this project targets a real folder instead of neatly prepared practice files. Only by facing real data do you meet real edge cases. Korean filenames, names with spaces, odd files with no extension — they are guaranteed to appear. Handling those unfamiliar inputs is this project’s hidden goal. The attitude of not panicking at unexpected input but saying "ah, an edge case" and adding one more if to your code — that is the true training this project offers.
(That said, if diving into your real Downloads folder feels like too much at first, you can make a practice folder and drop in a variety of files. The flow is the same.)
2-4. Edge Cases — Poking Holes in a Perfect Plan
At the design stage, let’s doubt ourselves once: "Will every file really move cleanly?"
- Files with no extension? (like
README) → Which folder should they go to? - A file with the same name already exists at the destination? → Overwrite? Fail?
- A folder itself gets mixed into the list? → Do we move a folder into a folder?
These are called edge cases. Today we write code that handles all three. "Worrying first about things that don’t normally happen" — this, too, is the security mindset. Attackers always aim for the edge cases.
3. Follow Along
3-1. Specifying the Target Folder — Environment Variables
First, put the Downloads folder’s address into a variable:
$src = "$env:USERPROFILE\Downloads"
$src
C:\Users\dlqht\Downloads
(Your own username will appear.)
Something new — $env:USERPROFILE: a variable Windows creates in advance (these are called environment variables), holding the address of "the current user’s home folder." Using it means you never have to edit the code no matter whose name is on the account. Portability — code that runs unchanged on other computers — is the first technique you’re adding here.
3-2. Getting the File List — Files Only, No Folders
$files = Get-ChildItem $src -File
$files.Count
6
(The count depends on the state of your folder.)
The -File option is the point: it fetches files only and excludes folders. This single option preemptively blocks edge case ③ from Section 2-4 ("the accident of moving a folder").
What if the list is 0? If the folder is empty, create a few practice files. Anything will do (save a.txt from Notepad, b.png from Paint, etc.).
3-3. Checking the Extension — The File’s Tail Tag
$files[0].Extension
.pdf
How to read the output: the extension of the first file appears. A file object carries more than just its name, and .Extension is the "extension" among them. Try printing .Name (full name) and .Length (size), too.
Why do this: we’ll use this extension as the folder name. .pdf files go into the PDF folder. That tail tag alone is our classification criterion.
3-4. Safety First — A -WhatIf Rehearsal
Before full assembly, let’s put today’s core habit into your body. Pick any one file and move it for pretend:
Move-Item $files[0].FullName "$src\TEST_FOLDER\" -WhatIf
WhatIf: 대상 "항목: C:\Users\...\Downloads\report.pdf 대상: C:\Users\...\Downloads\TEST_FOLDER\"에서 "파일 이동" 작업을 수행합니다.
(Verified on 2026-09-09. The message displays both the source path and the destination path.)
How to read the output: it says "performing the operation," but nothing actually happened. Check the folder — the file is still there. That’s -WhatIf: a rehearsal that pretends to run for real and only shows the result.
Why do this: mistakes in file-move/delete scripts are hard to undo. Especially inside a loop, one bug can wreck dozens of files at once. Whenever you use the "loop + file manipulation" combination, -WhatIf first, no exceptions — make sure this sticks to you today.
3-5. Full Assembly — Completing the Classifier
Now let’s assemble everything into one script. Write the following in Notepad and save it as organize.ps1 (encoding: UTF-8 with BOM!):
# 파일 자동 분류기 - 대상 폴더를 확장자별로 정리
$src = "$env:USERPROFILE\Downloads"
$files = Get-ChildItem $src -File
foreach ($f in $files) {
$ext = $f.Extension
if ($ext -eq "") { $ext = "NOEXT" } # 예외 1: 확장자 없는 파일
$folderName = $ext.TrimStart(".").ToUpper() # ".pdf" -> "PDF"
$dest = "$src\$folderName"
if (-not (Test-Path $dest)) {
New-Item -ItemType Directory -Path $dest | Out-Null
}
Move-Item $f.FullName $dest -WhatIf # 첫 실행은 미리보기!
}
Three new expressions explained:
$ext.TrimStart(".").ToUpper()— strips the leading dot from.pdf(TrimStart) and uppercases it (ToUpper) →PDF. Processing that makes the folder name tidy-not (Test-Path $dest)— "if that folder does not exist" — Step 8’s if withTest-Path(existence check) inside| Out-Null— discards New-Item’s output. A device for creating quietly (a new use of the pipe!)
3-6. Rehearsal → Showtime
First, run it with -WhatIf in place:
.\organize.ps1
WhatIf: 대상 "항목: ...\photo.jpg 대상: ...\Downloads\JPG\photo.jpg"에서 "파일 이동" 작업을 수행합니다.
WhatIf: 대상 "항목: ...\README 대상: ...\Downloads\NOEXT\README"에서 "파일 이동" 작업을 수행합니다.
WhatIf: 대상 "항목: ...\report.pdf 대상: ...\Downloads\PDF\report.pdf"에서 "파일 이동" 작업을 수행합니다.
...
(Verified on 2026-09-09: the extension-less README was sorted into NOEXT, and the Korean filename "내 문서.txt" was correctly sorted into TXT as well.)
What to check: ① is each file headed to the correct folder name, ② are there any odd destinations (empty names, strange paths)? If you spot a problem, fix the code and rehearse again.
Good to know: although this is a rehearsal, the folders do actually get created. That’s because -WhatIf is attached only to Move-Item, while the New-Item that creates folders runs as normal. A few empty folders appearing is expected; no files have been moved.
Once you’re satisfied, delete -WhatIf from the script, save, and run again:
.\organize.ps1
This time it finishes quietly (no output = normal). Open the folder — subfolders by extension should now exist, with your files neatly sorted into them.
Predict this: what happens if you run the script one more time? (Answer: no files remain at the top level, so
$filesis 0 and nothing happens. Verified on 2026-09-09. Whether re-running an already-cleaned state is safe — this is called "idempotency," an important quality for automation scripts. Check it yourself.)
4. Missions & Exercises
Mission — Upgrading the Classifier
Add the following features to your finished classifier, one at a time:
- Report output: when sorting finishes, print
"총 N개 파일을 M개 폴더로 정리했습니다"(hint:$files.Countand an accumulator variable) - Name-collision handling: if a file with the same name exists at the destination, skip it and print
"건너뛰기: 파일명"(hint:Test-Path "$dest\$($f.Name)"and if/else) - Change the target: change
$srcto organize the Desktop ($env:USERPROFILE\Desktop) and run
Exercises
Question 1. What exactly does -WhatIf do?
Question 2. What benefit does using $env:USERPROFILE give your code?
Question 3. What happens in this script if you leave an extension-less file (like README) as is? How did we handle it?
Question 4. How does Move-Item behave when a file with the same name already exists in the destination folder?
5. Model Answers & Completion Criteria
Mission Walkthrough
Feature 1 — Report output: put $moved = 0 at the top of the script, and add $moved++ (increment by 1) on the line after Move-Item. After the loop ends, print like this:
"총 $($files.Count)개 파일을 정리 대상으로 처리했습니다 (이동: $moved 개)"
For the folder count, collect the $folderName values and count them after removing duplicates (hint: gather them in an empty array @() and use Select-Object -Unique).
Feature 2 — Name-collision handling: right before Move-Item, check whether the same name exists inside the destination:
if (Test-Path "$dest\$($f.Name)") {
"건너뛰기: $($f.Name)"
} else {
Move-Item $f.FullName $dest
}
How to verify: to test feature 2, deliberately create files with the same name in both the destination folder and the target folder, then run. If the "건너뛰기" (skip) message appears and the original wasn’t overwritten, you’ve succeeded.
Feature 3 — Change the target: just change one line to $src = "$env:USERPROFILE\Desktop". Thanks to the environment variable, the rest of the code needs no changes — the power of portability from Section 2-3.
Advanced: register this script in Task Scheduler (Step 30) and it becomes "automatic nightly cleanup." At that point, remove -WhatIf — nobody is there to rehearse an automated run. Instead, the answer is to make it log results to a file (the techniques from Steps 4 and 5).
Exercise Answers
Answer 1. A preview switch that does not actually execute the command, but only shows as a message what would have happened. It’s a safety device for checking results in advance before hard-to-undo commands like file move, delete, or modify.
Answer 2. Since you don’t hard-code the username into the code, it runs unchanged on anyone’s computer (portability). Windows automatically fills in the home folder path of the currently logged-in user.
Answer 3. Without an extension, $ext becomes the empty string "", and the script tries to create a strange folder with an empty name, or throws an error. So we handled extension-less files with one line, if ($ext -eq "") { $ext = "NOEXT" }, placing them in a NOEXT folder. "Singling out unusual input first and treating it separately" is the standard shape of exception handling.
Answer 4. It does not overwrite; it stops with the error Cannot create a file when that file already exists. (verified on 2026-09-09 — this message appears in English even on Korean Windows). "Not overwriting by default" is a design that protects your precious originals. As in Mission 2, you can handle it cleanly by adding logic that checks for collisions with Test-Path first and skips.
Completion Criteria Checklist
- [ ] I can write out the algorithm in plain language before coding
- [ ] I can explain the purpose of environment variables like
$env:USERPROFILE - [ ] I can explain how
-WhatIfworks and actually use it - [ ] I can handle edge cases like extension-less files in code
- [ ] I ran the classifier script and organized a real folder
- [ ] I can explain why re-running is safe (idempotency)
- [ ] I completed at least one of the mission upgrades
6. Common Pitfalls & Fixes
Wall 1. I ran the script and nothing happened
Symptom: no error, no result.
Candidate causes: ① you forgot to remove -WhatIf (if no rehearsal messages appear either, this isn’t it), ② the target folder has no files (everything already sorted — the same situation as the idempotency check in Section 3-6), ③ the $src path is wrong.
Fix: print $src and $files.Count at the top of the script. Once you can see "where is it looking, how many did it find," the cause reveals itself immediately.
Wall 2. Folder names come out weird
Symptom: folders with dots like .PDF, or lowercase folder names.
Cause: you omitted the TrimStart(".") and ToUpper() processing, or used them in the wrong order.
Fix: print "$ext -> $folderName" during the rehearsal and visually confirm the processed result. Data processing is "make it, then always check it."
Wall 3. Errors caused by same-name files
Symptom: the error Cannot create a file when that file already exists. (this message appears in English even on Korean Windows — verified on 2026-09-09).
Cause: a file with the identical name already exists in the destination folder. By default, Move-Item stops without overwriting.
Fix: add the "skip on collision" logic from Mission 2. (In a hurry, -Force can overwrite, but we don’t recommend it — it can’t be undone. Think about why "not overwriting is the default.")
Wall 4. The folder name comes out empty because of an extension-less file
Symptom: a nameless folder tries to get created, or an error occurs.
Cause: edge case ① — for a file with no extension, $ext is the empty string "".
Fix: check that the line if ($ext -eq "") { $ext = "NOEXT" } exists in your assembled code. This one line is the standard shape of exception handling — "single out unusual input first and treat it separately."
Wall 5. I moved files to the wrong place and want to undo it
Symptom: files ended up in the wrong folders.
Cause: you ran without rehearsing, or there was a bug in the folder-name processing logic.
Fix: don’t panic — unlike copying, moving leaves no original behind, but you can restore things by using Move-Item again on whole folders. And the lesson: next time, -WhatIf first, no exceptions. Hit this wall once, and the rehearsal habit stays with you for life.
7. Summary
Today’s Skill Assembly Map
| Ingredient | Source | Today’s role |
|---|---|---|
| Variables & environment variables | Step 7 | Storing paths ($env:USERPROFILE) |
| Get-ChildItem | Step 2 | File list (files only, with -File) |
| foreach + if | Step 8 | Classification decision per file |
| New-Item / Move-Item | Step 2, extended | Creating folders, moving files |
| Script execution | Step 9 | Reuse via .\organize.ps1 |
Today’s New Tools
| Tool | What to remember |
|---|---|
-WhatIf |
Rehearsal before dangerous commands — no actual changes |
Test-Path |
Existence check (true/false) |
$f.Extension, etc. |
Properties of file objects |
TrimStart / ToUpper |
String processing — always visually confirm after processing |
A Sense That Matters More Than Commands
First, the -WhatIf habit: on an incident response scene, "irreversible operations" on evidence are taboo. The preview-first habit is the training for that. Second, edge-case thinking: attackers always look for "input the designer never imagined." Today’s single if handling the extension-less file was your first taste of defensive programming.
And remember the dual nature of automation. Today you sorted files in one second, but malware encrypts (ransomware) or steals thousands of files at exactly the same speed. Automation is a tool, and the tool’s direction is set by the person wielding it. What you built today is a miniature of the automatic log-and-evidence classification scripts used in a Security Operations Center (SOC).
Once every box is checked, Step 10 is complete. Click the checkbox in the sidebar to save your progress.