Merge CSV files and keep the source filenames
Merge CSV files with the same column structure in order and add a source_file column. Use small datasets to check item names containing commas, missing columns, and existing output.
Scan subfolders and record file paths, extensions, sizes, and modification times in a table. Start with 4 small example files while keeping the originals and existing results intact.
This translation was generated by AI. Check the code, units, and numbers against the original. Native-speaker review has not yet been completed for each language. 한국어
Who this is forPython beginners who have been manually listing the number and locations of documents in a folder
Creating an output folder is not enough to call this task complete. The CSV must contain each of the four original files exactly once, the listed sizes must total 63 bytes, and the original contents must remain unchanged. Check these conditions with the included data before moving on to your own work folder.
python example.pyThe code starts from the location of example.py, not the current terminal folder. It scans only sample_files under BASE, so you can move the whole example folder as long as its internal layout stays the same. outputs sits outside the scanned folder so the result CSV does not enter the input list on a later run.
| Column | Meaning | What to check |
|---|---|---|
| relative_path | Path relative to sample_files | Files with the same name are distinguishable if they are in different folders. |
| extension | The last extension, in lowercase | No extension is shown as (없음); tar.gz is shown as .gz. |
| size_bytes | File size as an integer number of bytes | 0 means an empty file, not a missing value. |
| modified_utc | File modification time in UTC | The trailing +00:00 means UTC, which differs from Korean local time. |
relative_path uses forward slashes for readability across operating systems. Results are sorted by this path. They do not depend on the internal file traversal order, making it easier to compare row order for the same input.
collect_files gathers file information, and main writes a new CSV. The code does not create outputs until input checks are complete. If it encounters an unreadable folder or a link, it stops rather than saving a partial list as a successful result.
"""sample_files를 읽어 outputs/file_list.csv에 목록을 기록합니다. 원본은 변경하지 않습니다."""
import csv
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
BASE = Path(__file__).resolve().parent
INPUT = BASE / "sample_files"
OUTPUT = BASE / "outputs"
FIELDS = ["relative_path", "extension", "size_bytes", "modified_utc"]
def is_link(path):
# Windows 연결 디렉터리(junction)도 Python 3.12 이상에서는 거부합니다.
return path.is_symlink() or getattr(path, "is_junction", lambda: False)()
def collect_files(folder):
if is_link(folder) or not folder.is_dir():
raise ValueError("sample_files는 실제 폴더여야 합니다.")
rows = []
def stop_on_error(error):
raise error
# 하위 폴더까지 읽되, 링크를 따라 다른 폴더로 나가지 않습니다.
for root, dirs, files in os.walk(folder, followlinks=False, onerror=stop_on_error):
root = Path(root)
for name in dirs + files:
if is_link(root / name):
raise ValueError(f"링크는 처리하지 않습니다: {root / name}")
for name in sorted(files):
path = root / name
if not path.is_file():
raise ValueError(f"일반 파일이 아닙니다: {path}")
stat = path.stat()
rows.append({
"relative_path": path.relative_to(folder).as_posix(),
"extension": path.suffix.lower() or "(없음)",
"size_bytes": stat.st_size,
"modified_utc": datetime.fromtimestamp(
stat.st_mtime, timezone.utc
).isoformat(timespec="seconds"),
})
return sorted(rows, key=lambda row: row["relative_path"])
def main():
if OUTPUT.exists() or OUTPUT.is_symlink():
raise FileExistsError("outputs가 이미 있습니다. 기존 결과를 옮긴 뒤 실행하세요.")
rows = collect_files(INPUT) # 모두 읽은 뒤에만 출력 폴더를 만듭니다.
OUTPUT.mkdir()
target = OUTPUT / "file_list.csv"
# x는 기존 파일을 덮어쓰지 않는 모드입니다. BOM은 한글 CSV 열기에 도움을 줍니다.
with target.open("x", encoding="utf-8-sig", newline="") as stream:
writer = csv.DictWriter(stream, fieldnames=FIELDS)
writer.writeheader()
writer.writerows(rows)
print(f"완료: {len(rows)}개 파일, 합계 {sum(row['size_bytes'] for row in rows)}바이트")
print(target)
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except (OSError, ValueError) as error:
print(f"중지: {error}", file=sys.stderr)
raise SystemExit(2)
csv.DictWriter handles CSV delimiters and quoting. The file is saved with newline set to an empty string and a UTF-8 BOM. The output file is opened in x mode so it will not overwrite a file that already exists. This example first checks whether the outputs folder itself exists.
| relative_path | extension | size_bytes |
|---|---|---|
| docs/agenda.txt | .txt | 15 |
| empty.txt | .txt | 0 |
| readme.txt | .txt | 17 |
| reports/sales.csv | .csv | 31 |
The table totals 15 + 0 + 17 + 31 = 63 bytes. The terminal also displays “완료: 4개 파일, 합계 63바이트” (complete: four files, sixty-three bytes total). The first line of the CSV contains column names, so a spreadsheet shows five rows including the header. Do not forget to count empty.txt.
Failure conditions are part of the design. Do not remove the output-protection code just because it blocks a repeated run. To keep the previous results, rename outputs to a name containing a date before running again, or extract the ZIP into a new folder to start a new test.
Do not point the example at your entire work folder at first. Put a few representative files in a copy of sample_files. Path objects handle filenames containing Korean characters or spaces. Symbolic links and directory junctions that point elsewhere are outside the supported scope, so use a small copy containing regular files.
If the input folder is empty, the script creates a header-only CSV and reports a file count of 0. This differs from stopping because the folder is missing. An empty folder can be a valid work state, so distinguish the two when interpreting results.
| Symptom | Possible cause | Next step |
|---|---|---|
| python command not found | A problem with the Python launcher installation or path settings | Check python --version first, and try py on Windows. |
| sample_files must be a real folder | The folder is missing or a link is being used | Check that you extracted the entire ZIP and restore a real folder. |
| outputs already exists | Results from a previous run are still present | Move the existing results somewhere safe, then run again. |
| Korean text appears garbled | The program opening the file is interpreting its encoding incorrectly | Select UTF-8 when importing the CSV, and do not modify the original. |
| Read permission error | An inaccessible folder or locked path | Narrow the scope to an accessible practice copy first. |
The result contains file information observed during the run. It is neither a duplicate-content check nor a backup you can restore from. Two documents with the same file size can have different contents. If you need hash comparisons, define that as a separate task and add the required columns.
This beginner example does not cover other programs changing names or contents during a scan, network-drive delays, or collecting very large numbers of files in memory. A disk-write failure can leave an incomplete outputs folder, so check both the completion message and the number of result rows.
2026-09-19 · Windows 11 · CPython 3.12.14 · No additional packages · Run in a temporary copy of the distribution
Includes code, input data, and instructions. Extract the ZIP and read README.txt first.
Download example ZIPExample code, filenames, and input keys remain unchanged. Refer to the commands and checking steps in the translated article as well.
Practice materials created for this site · Keep your originals separately before running.
The explanations and examples were written for this site. See the official sources below for the related behavior and concepts.