Intermediate Python 📂 Modules · 2 of 6 197 min read

Python OS Module

Master Python's os module — the master key to your file system. Learn every essential method: getcwd, listdir, mkdir, makedirs, rename, remove, os.path.join, environment variables, and the star of the show — os.walk() with a full visual traversal diagram. Includes a path anatomy breakdown, five real-world scripts (disk usage, bulk rename, duplicate finder), pathlib comparison, and 7 golden rules for cross-platform safety.

🌐
SectionSection第 01 节

The Story That Explains the os Module 🔑os Module समझाने वाली कहानी 🔑一个讲清 os 模块的故事 🔑

The Caretaker with Keys to Every Roomहर कमरे की चाबी वाला केयरटेकर握有每扇门钥匙的管理员
Imagine a huge office building. Visitors use the front door, and staff use their own desks. But one person can go anywhere — the caretaker. They hold the master key: they can open any room, list every room on every floor, check which lights are on, change a nameplate, or clear a room out completely.

Python's os module is that master key for your computer. It can show the current folder, list a folder's contents, create, rename and delete files and folders, walk through a whole folder tree, read environment variables and ask about the running program.

You don't need os for pure calculation — that is what math and collections are for. You need it the moment your program has to deal with the outside world: the file system, the settings it was started with, and the computer it is running on.
सोचिए एक बहुत बड़ी office building। मेहमान सामने के दरवाज़े से आते हैं, और कर्मचारी अपनी डेस्क पर बैठते हैं। पर एक इंसान कहीं भी जा सकता है — केयरटेकर। उसके पास master key है: वह कोई भी कमरा खोल सकता है, हर मंज़िल के हर कमरे की सूची बना सकता है, देख सकता है कौन-सी बत्ती जल रही है, नाम की पट्टी बदल सकता है, या कमरा पूरी तरह ख़ाली कर सकता है।

Python का os module आपके कंप्यूटर की वही master key है। यह मौजूदा folder दिखा सकता है, folder की सामग्री की सूची बना सकता है, files और folders बना, बदल और delete कर सकता है, पूरे folder tree में घूम सकता है, environment variables पढ़ सकता है और चल रहे program के बारे में पूछ सकता है।

सिर्फ़ गणना के लिए os की ज़रूरत नहीं — उसके लिए math और collections हैं। इसकी ज़रूरत तब पड़ती है जब आपके program को बाहरी दुनिया से निपटना हो: file system, जिन settings के साथ वह शुरू हुआ, और जिस कंप्यूटर पर वह चल रहा है।
想象一栋巨大的办公楼。访客走正门,员工坐在自己的工位上。但有一个人可以去 任何地方 — 管理员。他握有万能钥匙:能打开任何房间,列出每层楼的每个房间,查看哪些灯亮着,更换门牌,或者把房间彻底清空。

Python 的 os 模块 就是你电脑的万能钥匙。它能告诉你当前文件夹、列出文件夹内容、创建/重命名/删除文件和文件夹、遍历整个文件夹树、读取环境变量,并查询正在运行的程序。

纯计算用不到 os — 那是 math 和 collections 的活。一旦程序要和 外部世界 打交道 — 文件系统、启动时的设置、所运行的电脑 — 你就需要它。

The os module is Python's bridge to your operating system. Its star, os.walk(), visits every folder in a tree — the reason a ten-line script can tidy up thousands of files.

os module आपके operating system तक Python का पुल है। इसका सितारा, os.walk(), tree के हर folder में जाता है — इसी वजह से दस lines की script हज़ारों files सँभाल सकती है।

os 模块是 Python 通往操作系统的桥梁。它的明星 os.walk() 会访问树中的每个文件夹 — 这就是十行脚本能整理成千上万个文件的原因。

One Module, Three Doors to the Outside Worldएक Module, बाहरी दुनिया के तीन दरवाज़े一个模块,通往外部世界的三扇门
Python, os, operating system🐍 script.pyimport os🔑📁files & foldersfiles और folders文件与文件夹⚙️environment variablesenvironment variables环境变量🖥️processes & systemprocesses और system进程与系统

Your script talks to os; os talks to the operating system — whether that is Windows, macOS or Linux.

आपकी script os से बात करती है; os operating system से — चाहे वह Windows हो, macOS या Linux।

你的脚本和 os 对话;os 再和操作系统对话 — 无论是 Windows、macOS 还是 Linux。

💡
The Core Insightमूल बात核心要点

Functions like os.mkdir, os.remove and os.listdir are thin wrappers around the operating system's own calls, so they work the same way on Windows, macOS and Linux. os.path is different: most of it (join, split, splitext) is pure text work that follows your system's path rules and never touches the disk. Only a few functions, like exists and getsize, actually look at real files.

os.mkdir, os.remove और os.listdir जैसे functions operating system की अपनी calls पर पतली परत हैं, इसलिए Windows, macOS और Linux पर एक जैसे चलते हैं। os.path अलग है: इसका ज़्यादातर हिस्सा (join, split, splitext) सिर्फ़ text का काम है जो आपके system के path नियम मानता है और disk को कभी नहीं छूता। सिर्फ़ कुछ functions, जैसे exists और getsize, असली files देखते हैं।

os.mkdir、os.remove、os.listdir 这类函数是对操作系统自身调用的薄封装,所以在 Windows、macOS 和 Linux 上用法一致。os.path 则不同:它的大部分(join、split、splitext)只是遵循系统路径规则的 文本 处理,从不访问磁盘。只有少数函数,如 exists 和 getsize,才会真正查看文件。

▶️
This Tutorial Has Four Simulators and a Quizइस Tutorial में चार Simulators और एक Quiz है本教程有四个模拟器和一个小测验

Look for the interactive boxes: the Folder Operations Story (Section 05), the Path Dissector (06), the os.walk Explorer (08) and the Environment Lab (10). Everything they show came from running real Python 3.13 on a real folder tree.

Interactive डिब्बे खोजें: Folder Operations Story (Section 05), Path Dissector (06), os.walk Explorer (08) और Environment Lab (10)। ये जो कुछ दिखाते हैं, वह असली folder tree पर असली Python 3.13 चलाकर आया है।

请留意这些互动框:文件夹操作故事(第 05 节)、路径解剖器(06)、os.walk 探索器(08)和 环境变量实验室(10)。它们显示的一切,都来自在真实文件夹树上运行真实的 Python 3.13。


SectionSection第 02 节

The Six Families of os Functions 👪os Functions के छह परिवार 👪os 函数的六大家族 👪

The os module is large, but almost every function belongs to one of six families. Know the families and you will always know where to look.

os module बड़ा है, पर लगभग हर function छह परिवारों में से एक का है। परिवार जान लें तो हमेशा पता रहेगा कि कहाँ देखना है।

os 模块很大,但几乎每个函数都属于六大家族之一。认识这些家族,你就总能知道去哪里找。

📁
FoldersFolders文件夹
create, list, removeबनाना, सूची, हटाना创建、列出、删除
getcwd, chdir, listdir, mkdir, makedirs, rmdir, removedirs — everything about the folders themselves.
getcwd, chdir, listdir, mkdir, makedirs, rmdir, removedirs — ख़ुद folders से जुड़ा सब कुछ।
getcwd、chdir、listdir、mkdir、makedirs、rmdir、removedirs — 一切与文件夹本身有关的操作。
📄
FilesFiles文件
rename, remove, inspectनाम बदलना, हटाना, जाँचना重命名、删除、查看
rename, replace, remove, stat. To read or write the contents, use open() or pathlib — os handles the file's life and details.
rename, replace, remove, stat। सामग्री पढ़ने-लिखने के लिए open() या pathlib इस्तेमाल करें — os file के जीवन और जानकारी को संभालता है।
rename、replace、remove、stat。读写文件 内容 请用 open() 或 pathlib — os 负责文件的生命周期和元数据。
🔗
PathsPaths路径
os.path
join, split, basename, dirname, splitext, exists. Text operations that follow your system's path rules.
join, split, basename, dirname, splitext, exists। Text के काम जो आपके system के path नियम मानते हैं।
join、split、basename、dirname、splitext、exists。遵循系统路径规则的文本操作。
⚙️
EnvironmentEnvironment环境
os.environ, os.getenv
Read and set environment variables — API keys, config paths, debug flags. The bridge between your code and the shell that started it.
Environment variables पढ़ना और set करना — API keys, config paths, debug flags। आपके code और उसे शुरू करने वाले shell के बीच का पुल।
读取和设置环境变量 — API 密钥、配置路径、调试开关。你的代码与启动它的 shell 之间的桥梁。
🖥️
Process & systemProcess और system进程与系统
IDs, CPUs, OS nameIDs, CPUs, OS नामID、CPU、系统名
os.name, getpid, cpu_count, process_cpu_count. To run other programs, use the subprocess module.
os.name, getpid, cpu_count, process_cpu_count। दूसरे programs चलाने के लिए subprocess module इस्तेमाल करें।
os.name、getpid、cpu_count、process_cpu_count。运行其他程序请用 subprocess 模块。
🔍
TraversalTraversal遍历
os.walk, os.scandir
Visit every file inside a folder tree. scandir lists one folder fast; walk goes all the way down. This is where the magic happens.
Folder tree के अंदर की हर file तक जाना। scandir एक folder जल्दी दिखाता है; walk पूरे नीचे तक जाता है। जादू यहीं होता है।
访问文件夹树中的每个文件。scandir 快速列出一个文件夹;walk 一路深入到底。魔法就在这里。

SectionSection第 03 节

Getting Oriented — Where Am I? 🧭दिशा पहचानना — मैं कहाँ हूँ? 🧭找准方位 — 我在哪里?🧭

All the examples in this tutorial run inside the same practice folder, which contains a small project/ tree, some photos, logs and loose files.

इस tutorial के सारे उदाहरण एक ही practice folder में चलते हैं, जिसमें एक छोटा project/ tree, कुछ photos, logs और खुली files हैं।

本教程所有示例都在同一个练习文件夹中运行,里面有一个小型 project/ 树、一些照片、日志和零散文件。

import os

# Where is my script running from?
print(os.getcwd())

# What is in this folder? (sorted, because the order is not guaranteed)
print(sorted(os.listdir()))

# List a DIFFERENT folder without moving
print(sorted(os.listdir("project")))

# Check before you touch
print(os.path.exists("data.csv"), os.path.isfile("data.csv"), os.path.isdir("data.csv"))
print(os.path.exists("missing.csv"))
print(os.path.isdir("project"))
OUTPUT
/home/aarav/work ['data.csv', 'demo.py', 'draft.txt', 'logs', 'photos', 'project', 'reports_archive', 'temp.log', 'temp_data'] ['.git', 'LICENSE', 'README.md', 'data', 'docs', 'main.py', 'src'] True True False False True
🔑
Check Before You Act — or Catch the Errorकरने से पहले जाँचें — या Error पकड़ें先检查再动手 — 或捕获错误

exists(), isfile() and isdir() let you choose a path before something fails. But the disk can change between your check and your action, so for real programs it is often safer to just try the operation and catch the specific error (FileExistsError, FileNotFoundError…). Also note: listdir() returns names in no particular order — sort them when order matters.

exists(), isfile() और isdir() से आप कुछ fail होने से पहले रास्ता चुन सकते हैं। पर आपकी जाँच और काम के बीच disk बदल सकती है, इसलिए असली programs में अक्सर बेहतर है कि सीधे काम करें और ख़ास error पकड़ें (FileExistsError, FileNotFoundError…)। यह भी ध्यान दें: listdir() नाम किसी तय क्रम में नहीं देता — क्रम ज़रूरी हो तो sort करें।

exists()、isfile()、isdir() 让你在出错前选择路线。但在检查和操作之间磁盘可能发生变化,所以真实程序里往往直接执行操作并捕获具体错误(FileExistsError、FileNotFoundError……)更安全。另外注意:listdir() 返回的名字没有特定顺序 — 需要顺序时请排序。


SectionSection第 04 节

Creating and Removing Folders 🏗️Folders बनाना और हटाना 🏗️创建与删除文件夹 🏗️

import os
import shutil

os.mkdir("reports")                          # ONE folder
os.makedirs("archive/2026/january/raw")      # a whole chain at once
os.makedirs("cache", exist_ok=True)          # no error if it already exists
os.makedirs("cache", exist_ok=True)          # ...so running it twice is fine

for action in [lambda: os.mkdir("reports"),            # already exists
               lambda: os.mkdir("logs2/2026/jan"),     # parent missing
               lambda: os.rmdir("archive")]:           # not empty
    try:
        action()
    except OSError as e:
        print(type(e).__name__, "->", e)

os.rmdir("reports")                              # empty -> removed
os.removedirs("archive/2026/january/raw")        # removes the empty chain, bottom-up
print(os.path.exists("archive"))

open("cache/data.bin", "w").close()
shutil.rmtree("cache")                           # deletes a folder AND everything in it
print(os.path.exists("cache"))
OUTPUT
FileExistsError -> [Errno 17] File exists: 'reports' FileNotFoundError -> [Errno 2] No such file or directory: 'logs2/2026/jan' OSError -> [Errno 39] Directory not empty: 'archive' False False
🧠 Pick the Right Tool on Purposeसोच-समझकर सही औज़ार चुनें有意识地选对工具
mkdir
One folder. Fails if the parent is missing or the folder already exists. Strict and precise. एक folder। Parent ग़ायब हो या folder पहले से हो तो fail। सख़्त और सटीक। 一个文件夹。父目录不存在或文件夹已存在时失败。严格而精确。
makedirs
A chain of folders. Creates every missing parent. Add exist_ok=True so running it twice is safe. Folders की chain। हर ग़ायब parent बनाता है। exist_ok=True जोड़ें ताकि दो बार चलाना सुरक्षित हो। 一串文件夹。创建所有缺失的父目录。加上 exist_ok=True,运行两次也安全。
rmdir
Empty folders only. Fails if anything is inside — that is a safety feature. सिर्फ़ ख़ाली folders। अंदर कुछ भी हो तो fail — यह सुरक्षा feature है। 仅限空文件夹。里面有任何东西就失败 — 这是安全特性。
removedirs
Empty chain, bottom-up. Removes the folder, then each parent that becomes empty. ख़ाली chain, नीचे से ऊपर। Folder हटाता है, फिर हर parent जो ख़ाली हो जाए। 空链,自底向上。删除文件夹,再删除每个因此变空的父目录。
rmtree
From shutil. Deletes a folder and everything inside it. No undo, no recycle bin. shutil से। Folder और उसके अंदर का सब कुछ delete करता है। कोई undo नहीं, कोई recycle bin नहीं। 来自 shutil。删除文件夹及其中 一切。无法撤销,没有回收站。

SectionSection第 05 节

File Operations — Rename, Remove, Inspect 📄File के काम — नाम बदलना, हटाना, जाँचना 📄文件操作 — 重命名、删除、查看 📄

import os
from datetime import datetime, timezone

# Rename and move use the same call
os.rename("draft.txt", "final.txt")
os.rename("final.txt", "reports_archive/final.txt")   # into another folder
print(sorted(os.listdir("reports_archive")))

# Delete a file - permanent, no recycle bin
os.remove("temp.log")
print(os.path.exists("temp.log"))

# Metadata: size, timestamps, permissions
info = os.stat("data.csv")
print(f"Size:     {info.st_size} bytes")
print(f"Modified: {info.st_mtime}")                  # seconds since 1 Jan 1970 (UTC)
print(f"Mode:     {oct(info.st_mode)}")
when = datetime.fromtimestamp(info.st_mtime, tz=timezone.utc)
print(f"Readable: {when:%Y-%m-%d %H:%M} UTC")
OUTPUT
['final.txt'] False Size: 16 bytes Modified: 1767225600.0 Mode: 0o100644 Readable: 2026-01-01 00:00 UTC

st_mtime is a count of seconds since 1 January 1970 (UTC), often called a timestamp. Convert it with datetime.fromtimestamp(). The mode 0o100644 means "a regular file that the owner can read and write, and everyone else can read".

st_mtime 1 जनवरी 1970 (UTC) से बीते सेकंडों की गिनती है, जिसे अक्सर timestamp कहते हैं। इसे datetime.fromtimestamp() से बदलें। Mode 0o100644 का मतलब है "एक सामान्य file जिसे owner पढ़-लिख सकता है, और बाक़ी सब पढ़ सकते हैं"।

st_mtime 是自 1970 年 1 月 1 日(UTC)以来的秒数,通常叫 时间戳。用 datetime.fromtimestamp() 转换。模式 0o100644 表示"普通文件,所有者可读写,其他人只读"。

Try It — Folder Operations Storyकरके देखें — Folder Operations Story动手试试 — 文件夹操作故事

Three short stories, each a sequence of real calls run on a real empty folder. Step through and watch the tree change after every call — green rows appear, red rows disappear, and errors are shown exactly as Python reported them.

तीन छोटी कहानियाँ, हर एक असली ख़ाली folder पर चलाई गई असली calls का क्रम। कदम-दर-कदम चलें और हर call के बाद tree बदलते देखें — हरी rows आती हैं, लाल rows जाती हैं, और errors वैसे ही दिखते हैं जैसे Python ने बताए।

三个小故事,每个都是在真实空文件夹上运行的一系列真实调用。逐步执行,看每次调用后树的变化 — 绿色行出现,红色行消失,错误与 Python 报告的完全一致。

🏗️ Folder Operations StoryEvery call changes the tree — or raises an error.हर call tree बदलती है — या error देती है।每次调用都会改变树 — 或者报错。

SectionSection第 06 节

Path Anatomy — What Makes Up a Path 🧬Path की बनावट — Path किससे बनता है 🧬路径解剖 — 路径由什么组成 🧬

Every path can be split into named parts, and os.path has a function for each part.

हर path को नाम वाले हिस्सों में बाँटा जा सकता है, और os.path में हर हिस्से के लिए एक function है।

每个路径都能拆成有名字的部分,os.path 为每个部分都提供了函数。

Dissecting /home/aarav/data/report.csv/home/aarav/data/report.csv का विच्छेदन解剖 /home/aarav/data/report.csv
path anatomy/home/aarav/data/report.csvdirname()'/home/aarav/data'basename()'report.csv'splitext()[0]'/home/aarav/data/report'[1]'.csv'

split() gives (dirname, basename) in one call; splitext() splits off the extension; join() puts the parts back together.

split() एक call में (dirname, basename) देता है; splitext() extension अलग करता है; join() हिस्सों को वापस जोड़ता है।

split() 一次给出 (dirname, basename);splitext() 拆出扩展名;join() 把各部分重新拼起来。

import os

path = "/home/aarav/data/report.csv"

print(os.path.basename(path))
print(os.path.dirname(path))
print(os.path.split(path))          # (dirname, basename)
print(os.path.splitext(path))       # (everything before the extension, extension)

print(os.path.join("/home/aarav", "reports", "2026", "jan.csv"))
print(os.path.isabs(path), os.path.isabs("data.csv"))
OUTPUT
report.csv /home/aarav/data ('/home/aarav/data', 'report.csv') ('/home/aarav/data/report', '.csv') /home/aarav/reports/2026/jan.csv True False

Four Path SurprisesPath के चार चौंकाने वाले नियम路径的四个意外

import os
import ntpath          # the rules os.path uses on Windows, available everywhere

# 1) An absolute part throws away everything before it
print(os.path.join("data", "/etc/passwd"))

# 2) normpath tidies "..", "." and doubled slashes
print(os.path.normpath("data//2026/./jan/../feb/sales.csv"))

# 3) splitext only takes the LAST extension
print(os.path.splitext("backup.tar.gz"))
print(os.path.splitext(".bashrc"))           # a leading dot is not an extension

# 4) The same join on Windows rules
print(ntpath.join("C:\\Users", "aarav", "notes.txt"))
print(ntpath.splitext("C:\\Users\\aarav\\report.CSV"))
OUTPUT
/etc/passwd data/2026/feb/sales.csv ('backup.tar', '.gz') ('.bashrc', '') C:\Users\aarav\notes.txt ('C:\\Users\\aarav\\report', '.CSV')

Try It — Path Dissectorकरके देखें — Path Dissector动手试试 — 路径解剖器

Pick a path and a rule set. The coloured bar shows how Python splits it (folder · separator · name · extension), and the table shows the exact values Python returns. Try the Windows paths with Linux/macOS rules — a surprise awaits.

एक path और नियमों का सेट चुनें। रंगीन पट्टी दिखाती है कि Python उसे कैसे बाँटता है (folder · separator · नाम · extension), और table वही values दिखाती है जो Python लौटाता है। Windows paths को Linux/macOS नियमों से आज़माएँ — एक हैरानी इंतज़ार में है।

选择一个路径和一套规则。彩色条显示 Python 如何拆分它(文件夹 · 分隔符 · 名字 · 扩展名),表格显示 Python 返回的确切值。用 Linux/macOS 规则试试 Windows 路径 — 有惊喜等着你。

🧬 Path DissectorSame text, different rules — Linux/macOS vs Windows.वही text, अलग नियम — Linux/macOS बनाम Windows।同样的文本,不同的规则 — Linux/macOS 与 Windows。
rulesनियम规则
🚨
Build Paths with join, Not +Paths join से बनाएँ, + से नहीं用 join 而不是 + 构建路径

"data/" + name mostly works — Windows accepts / in most places — but string gluing causes real bugs: doubled or missing slashes, Windows paths built with \ that break on Linux, and paths shown to users in the wrong style. os.path.join() (or pathlib's /) uses the right separator and handles the edge cases. Just remember surprise #1: a part that starts with / throws away everything before it.

"data/" + name ज़्यादातर चलता है — Windows ज़्यादातर जगह / स्वीकार करता है — पर strings जोड़ने से असली bugs होते हैं: दोहरे या ग़ायब slashes, \ से बने Windows paths जो Linux पर टूटते हैं, और users को ग़लत शैली में दिखते paths। os.path.join() (या pathlib का /) सही separator इस्तेमाल करता है और किनारे के मामले संभालता है। बस हैरानी #1 याद रखें: / से शुरू होने वाला हिस्सा अपने पहले का सब फेंक देता है।

"data/" + name 大多数时候能用 — Windows 在大多数地方也接受 / — 但拼接字符串会带来真实的 bug:斜杠重复或缺失、用 \ 拼出的 Windows 路径在 Linux 上失效、给用户看的路径风格不对。os.path.join()(或 pathlib 的 /)会使用正确的分隔符并处理边界情况。只要记住意外 #1:以 / 开头的部分会丢弃它前面的一切。


SectionSection第 07 节

os.walk() — Visiting Every Folder in a Tree 🌳os.walk() — Tree के हर Folder तक जाना 🌳os.walk() — 访问树中的每个文件夹 🌳

os.walk(top) is a generator. Each time the loop asks for the next item, it visits one folder and hands back a 3-item tuple: (dirpath, dirnames, filenames). It keeps going until every folder below top has been visited.

os.walk(top) एक generator है। हर बार जब loop अगला item माँगता है, यह एक folder में जाता है और 3 items का tuple देता है: (dirpath, dirnames, filenames)। यह तब तक चलता है जब तक top के नीचे का हर folder देख न लिया जाए।

os.walk(top) 是一个 生成器。循环每要一次下一个元素,它就访问一个文件夹,并交回一个三元组:(dirpath, dirnames, filenames)。直到 top 下的每个文件夹都被访问过才停止。

One Visit, Three Valuesएक दौरा, तीन values一次访问,三个值
os.walk yield shape📁 project/data📁 raw/📄 sales.csvdirpath'project/data'dirnames['raw']filenames['sales.csv']one yield = one folderएक yield = एक folder一次 yield = 一个文件夹

dirnames and filenames hold names only — not full paths. Join them with dirpath to get a usable path.

dirnames और filenames में सिर्फ़ नाम होते हैं — पूरे paths नहीं। काम का path पाने के लिए इन्हें dirpath से जोड़ें।

dirnames 和 filenames 里 只有名字 — 不是完整路径。要和 dirpath 拼接才能得到可用的路径。

import os

for dirpath, dirnames, filenames in os.walk("project"):
    print(f"Folder: {dirpath}")
    print(f"  Sub-folders: {dirnames}")
    print(f"  Files:       {filenames}")
OUTPUT
Folder: project Sub-folders: ['src', '.git', 'data', 'docs'] Files: ['main.py', 'README.md', 'LICENSE'] Folder: project/src Sub-folders: ['api', '__pycache__'] Files: ['module.py', 'utils.py'] Folder: project/src/api Sub-folders: [] Files: ['README.md', 'routes.py'] Folder: project/src/__pycache__ Sub-folders: [] Files: ['utils.cpython-313.pyc'] Folder: project/.git Sub-folders: [] Files: ['config', 'HEAD'] Folder: project/data Sub-folders: ['raw'] Files: ['sales.csv'] Folder: project/data/raw Sub-folders: [] Files: ['sales_2025.csv'] Folder: project/docs Sub-folders: [] Files: ['guide.md', 'intro.md']
🔀
Look Closely at That Orderउस क्रम को ध्यान से देखें仔细看这个顺序

This is real output, and the order is not alphabetical: ['src', '.git', 'data', 'docs']. os.walk and os.listdir return names in whatever order the file system stores them. It can differ between computers, file systems and even runs. If your output must be stable (tests, reports, diffs), sort it.

यह असली output है, और क्रम वर्णानुसार नहीं है: ['src', '.git', 'data', 'docs']। os.walk और os.listdir नाम उसी क्रम में देते हैं जिसमें file system उन्हें रखता है। यह अलग कंप्यूटरों, file systems और यहाँ तक कि अलग runs में भी बदल सकता है। अगर output स्थिर चाहिए (tests, reports, diffs), तो sort करें।

这是真实输出,而且顺序 不是 字母序:['src', '.git', 'data', 'docs']。os.walk 和 os.listdir 按文件系统存储的顺序返回名字。不同电脑、不同文件系统、甚至不同次运行都可能不同。如果输出必须稳定(测试、报告、diff),请排序。

🧠 How os.walk Moves (topdown=True, the default)os.walk कैसे चलता है (topdown=True, default)os.walk 如何移动(topdown=True,默认)
1
Lists the top folder and yields it first. Top folder की सूची बनाता है और पहले उसे yield करता है। 列出 顶层 文件夹,并首先 yield 它。
2
Your loop body runs. You may change dirnames in place right now. आपके loop का body चलता है। आप अभी dirnames को उसी जगह बदल सकते हैं। 你的循环体运行。此刻你可以 就地修改 dirnames。
3
Goes into each name still left in dirnames, one by one, depth-first. dirnames में बचे हर नाम में एक-एक करके, गहराई पहले, जाता है। 按深度优先,逐一进入 dirnames 中剩下的每个名字。
4
Each sub-folder is yielded the same way, all the way down, until nothing is left. हर sub-folder इसी तरह yield होता है, पूरे नीचे तक, जब तक कुछ न बचे। 每个子文件夹都以同样方式 yield,一直到底,直到没有剩余。
🔗
Names, Not Paths — The #1 os.walk Bugनाम, Paths नहीं — os.walk का #1 Bug是名字,不是路径 — os.walk 头号 bug

os.remove(f) inside a walk loop looks for f in the current working directory, not in dirpath. It fails with FileNotFoundError — or, worse, deletes a different file with the same name. Always write os.path.join(dirpath, f).

Walk loop के अंदर os.remove(f) f को dirpath में नहीं, current working directory में खोजता है। यह FileNotFoundError देता है — या उससे बुरा, उसी नाम की कोई दूसरी file delete कर देता है। हमेशा os.path.join(dirpath, f) लिखें।

在 walk 循环里写 os.remove(f),会在 当前工作目录 而不是 dirpath 中找 f。结果是 FileNotFoundError — 更糟的是,删掉另一个同名文件。请始终写 os.path.join(dirpath, f)。


SectionSection第 08 节

Try It — The os.walk Explorer 🔭करके देखें — os.walk Explorer 🔭动手试试 — os.walk 探索器 🔭

This explorer replays real os.walk runs on the project/ tree from Section 07 — 12 combinations, all recorded from Python. Step through one yield at a time. The numbers on the folders show the visiting order. SKIP is {".git", "__pycache__"}.

यह explorer Section 07 के project/ tree पर असली os.walk runs दोहराता है — 12 combinations, सब Python से रिकॉर्ड किए गए। एक बार में एक yield आगे बढ़ें। Folders पर लिखे नंबर जाने का क्रम दिखाते हैं। SKIP है {".git", "__pycache__"}।

这个探索器重放在第 07 节 project/ 树上真实运行的 os.walk — 共 12 种组合,全部来自 Python 录制。一次前进一个 yield。文件夹上的数字表示访问顺序。SKIP 是 {".git", "__pycache__"}。

🔭 os.walk ExplorerReal yields, one at a time.असली yields, एक-एक करके।真实的 yield,逐个展示。
topdown
pruneprune剪枝
orderक्रम顺序
Current yieldमौजूदा yield当前 yield
Printed so farअब तक छपा已打印
currentमौजूदा当前visitedदेखा गया已访问pruned ✂️काटा गया ✂️已剪掉 ✂️
🎯
Three Experiments Worth Doingतीन प्रयोग जो ज़रूर करें值得一做的三个实验

① Pick dirnames = … and play to the end — .git is still visited. ② Pick dirnames[:] = … — now it is skipped. ③ Keep the slice but switch to topdown=False — pruning stops working, because the children were already visited before their parent was yielded.

① dirnames = … चुनें और अंत तक चलाएँ — .git अब भी देखा जाता है। ② dirnames[:] = … चुनें — अब यह छूट जाता है। ③ Slice रखें पर topdown=False पर जाएँ — pruning काम करना बंद कर देती है, क्योंकि parent के yield होने से पहले ही children देखे जा चुके होते हैं।

① 选 dirnames = … 播放到底 — .git 依然被访问。② 选 dirnames[:] = … — 这次被跳过了。③ 保留切片但切换到 topdown=False — 剪枝失效,因为父目录被 yield 之前,子目录早已访问完。

Predictable Order with dirnames.sort()dirnames.sort() से तय क्रम用 dirnames.sort() 获得可预测顺序

import os

# Sort in place for a predictable order on every computer
for dirpath, dirnames, filenames in os.walk("project"):
    dirnames.sort()
    print(dirpath, sorted(filenames))
OUTPUT
project ['LICENSE', 'README.md', 'main.py'] project/.git ['HEAD', 'config'] project/data ['sales.csv'] project/data/raw ['sales_2025.csv'] project/docs ['guide.md', 'intro.md'] project/src ['module.py', 'utils.py'] project/src/__pycache__ ['utils.cpython-313.pyc'] project/src/api ['README.md', 'routes.py']

SectionSection第 09 节

os.walk Patterns You Will Use Again and Again 🧰os.walk Patterns जो बार-बार काम आएँगे 🧰会反复用到的 os.walk 模式 🧰

Pattern 1 — Skip Folders (Pruning)Pattern 1 — Folders छोड़ना (Pruning)模式 1 — 跳过文件夹(剪枝)

os.walk decides where to go next by reading the same list object it gave you. Replace the contents of that list with dirnames[:] = … and the walk changes. Assign a brand-new list with dirnames = … and you only change your local variable — the walk never sees it.

os.walk आगे कहाँ जाना है, यह उसी list object को पढ़कर तय करता है जो उसने आपको दिया। dirnames[:] = … से उस list की सामग्री बदलें तो walk बदल जाता है। dirnames = … से नई list assign करें तो सिर्फ़ आपका local variable बदलता है — walk को कभी पता नहीं चलता।

os.walk 通过读取它交给你的 同一个列表对象 来决定下一步去哪里。用 dirnames[:] = … 替换列表内容,遍历就会改变。用 dirnames = … 赋一个新列表,只改变了你的局部变量 — 遍历根本看不到。

import os

skip = {".git", "__pycache__"}

print("--- WRONG: dirnames = [...] ---")
for dirpath, dirnames, filenames in os.walk("project"):
    dirnames.sort()
    dirnames = [d for d in dirnames if d not in skip]      # a new local list
    print(dirpath)

print("--- RIGHT: dirnames[:] = [...] ---")
for dirpath, dirnames, filenames in os.walk("project"):
    dirnames[:] = sorted(d for d in dirnames if d not in skip)   # same list, changed
    print(dirpath)
OUTPUT
--- WRONG: dirnames = [...] --- project project/.git project/data project/data/raw project/docs project/src project/src/__pycache__ project/src/api --- RIGHT: dirnames[:] = [...] --- project project/data project/data/raw project/docs project/src project/src/api

Pattern 2 — Find Files by TypePattern 2 — Type से Files खोजना模式 2 — 按类型查找文件

import os

py_files = []
for dirpath, dirnames, filenames in os.walk("project"):
    dirnames[:] = sorted(d for d in dirnames if d not in {".git", "__pycache__"})
    for f in sorted(filenames):
        if f.endswith(".py"):
            py_files.append(os.path.join(dirpath, f))   # join name + folder!

print(f"Found {len(py_files)} Python files:")
for p in py_files:
    print(" ", p)
OUTPUT
Found 4 Python files: project/main.py project/src/module.py project/src/utils.py project/src/api/routes.py

Pattern 3 — Delete Bottom-Up with topdown=FalsePattern 3 — topdown=False से नीचे से ऊपर Delete模式 3 — 用 topdown=False 自底向上删除

import os

# topdown=False visits the DEEPEST folders first -
# you can only rmdir a folder after its children are gone
for dirpath, dirnames, filenames in os.walk("temp_data", topdown=False):
    for f in filenames:
        os.remove(os.path.join(dirpath, f))
        print("removed file  ", os.path.join(dirpath, f))
    os.rmdir(dirpath)
    print("removed folder", dirpath)
OUTPUT
removed file temp_data/cache/deep/c.tmp removed folder temp_data/cache/deep removed file temp_data/cache/b.tmp removed folder temp_data/cache removed file temp_data/a.tmp removed folder temp_data
♻️
In Real Code, Use shutil.rmtreeअसली Code में shutil.rmtree इस्तेमाल करें真实代码中请用 shutil.rmtree

This loop shows why bottom-up order matters: a folder can only be removed after it is empty. For everyday work, shutil.rmtree(path) does the same thing in one line and handles more edge cases (links, read-only files on Windows).

यह loop दिखाता है कि नीचे-से-ऊपर का क्रम क्यों ज़रूरी है: folder तभी हटता है जब वह ख़ाली हो। रोज़ के काम के लिए shutil.rmtree(path) यही एक line में करता है और ज़्यादा किनारे के मामले संभालता है (links, Windows पर read-only files)।

这个循环展示了自底向上顺序 为什么 重要:文件夹只有清空后才能删除。日常工作中,shutil.rmtree(path) 一行就能完成,还处理更多边界情况(链接、Windows 上的只读文件)。

Pattern 4 — One Folder, Fast, with os.scandirPattern 4 — एक Folder, तेज़, os.scandir से模式 4 — 用 os.scandir 快速查看一个文件夹

os.scandir() returns DirEntry objects that already know their name, full path and type. Checking is_dir() usually costs no extra system call. os.walk itself is built on scandir.

os.scandir() DirEntry objects देता है जिन्हें अपना नाम, पूरा path और type पहले से पता होता है। is_dir() जाँचने में आमतौर पर कोई अतिरिक्त system call नहीं लगती। os.walk ख़ुद scandir पर बना है।

os.scandir() 返回 DirEntry 对象,它们已经知道自己的名字、完整路径和类型。检查 is_dir() 通常不需要额外的系统调用。os.walk 本身就构建在 scandir 之上。

import os

with os.scandir("project") as entries:
    for entry in sorted(entries, key=lambda e: e.name):
        kind = "folder" if entry.is_dir() else "file  "
        size = entry.stat().st_size if entry.is_file() else ""
        print(kind, entry.name, size)
OUTPUT
folder .git file LICENSE 12 file README.md 50 folder data folder docs file main.py 51 folder src

SectionSection第 10 节

Environment Variables ⚙️Environment Variables ⚙️环境变量 ⚙️

Environment variables are named text values that a program receives from whatever started it — your terminal, an IDE, Docker, or a cloud server. They are the standard place for settings that change between machines: ports, database URLs, API keys.

Environment variables नाम वाली text values हैं जो program को उसे शुरू करने वाले से मिलती हैं — आपका terminal, IDE, Docker, या cloud server। मशीन-दर-मशीन बदलने वाली settings के लिए यही मानक जगह है: ports, database URLs, API keys।

环境变量是带名字的文本值,程序从启动它的东西那里接收 — 你的终端、IDE、Docker 或云服务器。它们是存放随机器变化的设置的标准位置:端口、数据库 URL、API 密钥。

import os

home = os.environ["HOME"]                                   # KeyError if missing
db_url = os.getenv("DATABASE_URL", "sqlite:///dev.db")    # safe, with a default
port = os.getenv("PORT", "8080")
print(home, db_url)
print(repr(port), type(port).__name__)                     # always a string!
print(int(port) + 1)

os.environ["APP_MODE"] = "production"       # this process and its children only
os.environ["APP_REGION"] = "ap-south-1"
for key, value in sorted(os.environ.items()):
    if key.startswith("APP_"):
        print(f"{key}={value}")

try:
    os.environ["NO_SUCH_VAR"]
except KeyError as e:
    print("KeyError:", e)
OUTPUT
/home/aarav sqlite:///dev.db '8080' str 8081 APP_MODE=production APP_REGION=ap-south-1 KeyError: 'NO_SUCH_VAR'
Way to readपढ़ने का तरीका读取方式If missingग़ायब होने पर不存在时Use whenकब इस्तेमाल करें适用场景
os.environ["KEY"]KeyErrorthe program cannot run without itइसके बिना program चल ही नहीं सकता没有它程序无法运行
os.getenv("KEY")Noneit is optionalयह वैकल्पिक है它是可选的
os.getenv("KEY", "x")"x"there is a sensible defaultकोई समझदार default है有合理的默认值
os.environ["KEY"] = "v"creates itबना देता है创建它this process and the child processes it starts — never the parent shellयह process और इसके शुरू किए child processes — parent shell कभी नहीं本进程及其启动的子进程 — 永远不影响父 shell

Try It — Environment Labकरके देखें — Environment Lab动手试试 — 环境变量实验室

Set PORT and DEBUG the way a shell would, then see what config.py really prints. Every output here was recorded from Python with those exact variables set. Try DEBUG=0 and look at the last line.

PORT और DEBUG वैसे set करें जैसे shell करता है, फिर देखें config.py असल में क्या छापता है। यहाँ का हर output उन्हीं variables के साथ Python से रिकॉर्ड किया गया है। DEBUG=0 आज़माएँ और आख़िरी line देखें।

像 shell 那样设置 PORT 和 DEBUG,然后看 config.py 真正打印什么。这里每个输出都是在设置了这些变量后从 Python 录制的。试试 DEBUG=0,看最后一行。

⚙️ Environment LabEverything in os.environ is a string.os.environ में सब कुछ string है।os.environ 里的一切都是字符串。
PORT
DEBUG
ShellShellShell
config.pyconfig.pyconfig.py
OutputOutput输出
🧵
"0" and "false" Are Truthy"0" और "false" Truthy हैं"0" 和 "false" 都是真值

Any non-empty string is True, so bool("0") and bool("false") are both True. Compare the text instead: os.getenv("DEBUG", "0").lower() in {"1", "true", "yes"}. And convert numbers with int() inside a try, so a bad value gives a clear message.

हर ख़ाली-नहीं string True है, इसलिए bool("0") और bool("false") दोनों True हैं। इसके बजाय text की तुलना करें: os.getenv("DEBUG", "0").lower() in {"1", "true", "yes"}। और numbers को try के अंदर int() से बदलें, ताकि ग़लत value पर साफ़ संदेश मिले।

任何非空字符串都是 True,所以 bool("0") 和 bool("false") 都是 True。应该比较文本:os.getenv("DEBUG", "0").lower() in {"1", "true", "yes"}。数字请在 try 中用 int() 转换,这样坏值能给出清楚的提示。

🪟
HOME and USER Are Not on WindowsHOME और USER Windows पर नहीं होतेWindows 上没有 HOME 和 USER

Windows uses USERPROFILE and USERNAME. For portable code, use os.path.expanduser("~") or pathlib.Path.home() for the home folder, and getpass.getuser() for the user name.

Windows USERPROFILE और USERNAME इस्तेमाल करता है। हर system पर चलने वाले code के लिए home folder को os.path.expanduser("~") या pathlib.Path.home() से, और user name को getpass.getuser() से लें।

Windows 使用 USERPROFILE 和 USERNAME。要写可移植代码,主目录请用 os.path.expanduser("~") 或 pathlib.Path.home(),用户名用 getpass.getuser()。


SectionSection第 11 节

Process and System Information 🖥️Process और System की जानकारी 🖥️进程与系统信息 🖥️

import os
import subprocess
import sys

print(os.name)                         # 'posix' on Linux/macOS, 'nt' on Windows
print(sys.platform)                    # 'linux', 'darwin', 'win32'
print(os.getpid() > 0)                 # this process's ID (changes every run)
print(os.cpu_count())                  # CPUs in the machine
print(os.process_cpu_count())          # CPUs THIS process may use (Python 3.13+)

# Run another program and capture its output
result = subprocess.run([sys.executable, "--version"], capture_output=True, text=True)
print(result.returncode, result.stdout.strip())
OUTPUT
posix linux True 2 2 0 Python 3.13.16

os.cpu_count() counts the CPUs in the machine. os.process_cpu_count() (new in Python 3.13) counts the CPUs this process is allowed to use — which can be fewer inside a container or with CPU limits. Use it to size a worker pool.

os.cpu_count() मशीन के CPUs गिनता है। os.process_cpu_count() (Python 3.13 में नया) वे CPUs गिनता है जिन्हें यह process इस्तेमाल कर सकता है — container में या CPU limits के साथ ये कम हो सकते हैं। Worker pool का आकार तय करने के लिए इसे इस्तेमाल करें।

os.cpu_count() 统计机器的 CPU 数。os.process_cpu_count()(Python 3.13 新增)统计 本进程被允许使用的 CPU 数 — 在容器中或有 CPU 限制时可能更少。用它来决定工作池大小。

🛑
Prefer subprocess over os.systemos.system की जगह subprocess优先用 subprocess 而非 os.system

os.system("ls -la") passes a string to the shell: it only returns an exit code, it breaks on Windows (no ls there), and building the string from user input invites shell injection. subprocess.run([...], capture_output=True, text=True) takes a list of arguments, captures the output, and never goes through a shell unless you ask.

os.system("ls -la") एक string shell को देता है: यह सिर्फ़ exit code लौटाता है, Windows पर टूटता है (वहाँ ls नहीं है), और user input से string बनाना shell injection को न्योता देता है। subprocess.run([...], capture_output=True, text=True) arguments की list लेता है, output पकड़ता है, और जब तक आप न कहें, shell से नहीं गुज़रता।

os.system("ls -la") 把字符串交给 shell:只返回退出码,在 Windows 上会失败(那里没有 ls),而用用户输入拼字符串会招来 shell 注入。subprocess.run([...], capture_output=True, text=True) 接收参数列表、捕获输出,除非你要求,否则从不经过 shell。


SectionSection第 12 节

Real-World Scripts 🛠️असली काम की Scripts 🛠️实战脚本 🛠️

Five small scripts that solve problems people really have. Each one was run on the practice folder, and the output shown is the real output.

पाँच छोटी scripts जो लोगों की असली समस्याएँ हल करती हैं। हर एक practice folder पर चलाई गई, और दिखाया गया output असली है।

五个解决真实问题的小脚本。每个都在练习文件夹上运行过,显示的输出都是真实输出。

01
📊 Count files by extensionExtension के हिसाब से files गिनना按扩展名统计文件
Walk, skip junk folders, take splitext()[1], count with Counter.
Walk करें, फ़ालतू folders छोड़ें, splitext()[1] लें, Counter से गिनें।
遍历、跳过无用文件夹、取 splitext()[1]、用 Counter 计数。
02
📦 Measure a folderFolder का आकार नापना测量文件夹大小
Add getsize() of every file; skip files you cannot read.
हर file का getsize() जोड़ें; जो file न पढ़ी जा सके उसे छोड़ें।
累加每个文件的 getsize();跳过读不了的文件。
03
✏️ Rename safelyसुरक्षित नाम बदलना安全地重命名
Print what would change (dry run), check it, then run for real.
पहले छापें कि क्या बदलेगा (dry run), जाँचें, फिर असल में चलाएँ।
先打印 将要 改变什么(演练),检查后再真正执行。
04
🕒 Find recent filesहाल की files खोजना查找最近的文件
Compare getmtime() with a cutoff time.
getmtime() की तुलना cutoff समय से करें।
把 getmtime() 与截止时间比较。
05
👯 Find duplicate namesदोहराए नाम खोजना查找重名文件
Group full paths by file name in a defaultdict(list).
File नाम के हिसाब से पूरे paths को defaultdict(list) में समूह बनाएँ।
用 defaultdict(list) 按文件名分组完整路径。

1 · Count Files by Extension1 · Extension के हिसाब से Files गिनना1 · 按扩展名统计文件

import os
from collections import Counter

counts = Counter()
for dirpath, dirnames, filenames in os.walk("project"):
    dirnames[:] = [d for d in dirnames if d not in {".git", "__pycache__"}]
    for f in filenames:
        ext = os.path.splitext(f)[1].lower() or "(no ext)"
        counts[ext] += 1

for ext, n in counts.most_common():
    print(f"{ext:10s} {n}")
OUTPUT
.py 4 .md 4 .csv 2 (no ext) 1

2 · Total Size of a Folder2 · Folder का कुल आकार2 · 文件夹总大小

import os

def folder_size(root):
    total = 0
    for dirpath, _, filenames in os.walk(root):
        for f in filenames:
            try:
                total += os.path.getsize(os.path.join(dirpath, f))
            except OSError:
                pass                 # broken link, no permission...
    return total

size = folder_size("project")
print(f"{size:,} bytes ({size / 1024:.2f} KB)")
OUTPUT
801 bytes (0.78 KB)

3 · Bulk Rename with a Dry Run3 · Dry Run के साथ थोक में नाम बदलना3 · 带演练的批量重命名

import os

DRY_RUN = True          # look first, change later!

def rename_jpeg(root, dry_run):
    for dirpath, dirnames, filenames in os.walk(root):
        dirnames.sort()
        for f in sorted(filenames):
            if f.lower().endswith(".jpeg"):
                old = os.path.join(dirpath, f)
                new = os.path.join(dirpath, f[:-5] + ".jpg")
                print(("would rename " if dry_run else "renamed      ") + f"{old} -> {new}")
                if not dry_run:
                    os.rename(old, new)

rename_jpeg("photos", DRY_RUN)
rename_jpeg("photos", dry_run=False)
print(sorted(os.listdir("photos/trip")))
OUTPUT
would rename photos/home.jpeg -> photos/home.jpg would rename photos/trip/goa_1.jpeg -> photos/trip/goa_1.jpg would rename photos/trip/goa_2.jpeg -> photos/trip/goa_2.jpg renamed photos/home.jpeg -> photos/home.jpg renamed photos/trip/goa_1.jpeg -> photos/trip/goa_1.jpg renamed photos/trip/goa_2.jpeg -> photos/trip/goa_2.jpg ['goa_1.jpg', 'goa_2.jpg', 'map.png']
⚠️
os.rename Can Silently Overwriteos.rename चुपचाप Overwrite कर सकता हैos.rename 可能悄悄覆盖文件

On Linux and macOS, if the target name already exists, os.rename replaces it without asking (you saw this in the Operations Story, story 3). On Windows it raises FileExistsError instead. If you want overwriting on every system, use os.replace. If you want to protect existing files, check with os.path.exists(new) first.

Linux और macOS पर, अगर target नाम पहले से है, तो os.rename उसे बिना पूछे बदल देता है (Operations Story, कहानी 3 में आपने देखा)। Windows पर यह इसके बजाय FileExistsError देता है। हर system पर overwrite चाहिए तो os.replace इस्तेमाल करें। मौजूदा files बचानी हैं तो पहले os.path.exists(new) से जाँचें।

在 Linux 和 macOS 上,如果目标名已存在,os.rename 会 不加询问 地替换它(你在操作故事第 3 个故事中见过)。在 Windows 上则会抛出 FileExistsError。想在所有系统上都覆盖,请用 os.replace。想保护已有文件,请先用 os.path.exists(new) 检查。

4 · Files Changed in the Last 24 Hours4 · पिछले 24 घंटों में बदली Files4 · 过去 24 小时内修改的文件

import os
import time

cutoff = time.time() - 24 * 60 * 60          # 24 hours ago

recent = []
for dirpath, _, filenames in os.walk("logs"):
    for f in filenames:
        path = os.path.join(dirpath, f)
        if os.path.getmtime(path) > cutoff:
            recent.append(path)

print(f"{len(recent)} file(s) changed in the last 24 hours:", sorted(recent))
OUTPUT
2 file(s) changed in the last 24 hours: ['logs/app.log', 'logs/error.log']

Note: this checks the last 24 hours, not "since midnight". For "today", compare against datetime.combine(date.today(), time.min).timestamp().

ध्यान दें: यह पिछले 24 घंटे जाँचता है, "आधी रात से" नहीं। "आज" के लिए datetime.combine(date.today(), time.min).timestamp() से तुलना करें।

注意:这里检查的是过去 24 小时,不是"从午夜起"。要表示"今天",请与 datetime.combine(date.today(), time.min).timestamp() 比较。

5 · Same File Name in Different Folders5 · अलग Folders में एक ही File नाम5 · 不同文件夹中的同名文件

import os
from collections import defaultdict

seen = defaultdict(list)
for dirpath, dirnames, filenames in os.walk("project"):
    dirnames.sort()
    for f in filenames:
        seen[f].append(os.path.join(dirpath, f))

for name, paths in sorted(seen.items()):
    if len(paths) > 1:
        print(f"{name} appears in:")
        for p in paths:
            print(f"  {p}")
OUTPUT
README.md appears in: project/README.md project/src/api/README.md

SectionSection第 13 节

The Modern Alternative — pathlib 🌟आधुनिक विकल्प — pathlib 🌟现代替代方案 — pathlib 🌟

pathlib (standard library since Python 3.4) wraps a path in an object. Instead of passing strings to many os.path functions, you ask the path itself. Most new code uses pathlib; os is still the tool for environment variables, processes and low-level work — and you will read os.path code for years.

pathlib (Python 3.4 से standard library में) path को एक object में लपेटता है। कई os.path functions को strings देने के बजाय, आप path से ही पूछते हैं। ज़्यादातर नया code pathlib इस्तेमाल करता है; environment variables, processes और low-level काम के लिए os अब भी औज़ार है — और os.path वाला code आप सालों तक पढ़ेंगे।

pathlib(自 Python 3.4 起属于标准库)把路径包装成对象。你不再把字符串传给一堆 os.path 函数,而是直接问路径本身。大多数新代码使用 pathlib;而环境变量、进程和底层操作仍要靠 os — 而且你还会读很多年的 os.path 代码。

os.path
Taskकाम任务os / os.path
joinजोड़ना拼接os.path.join(a, "b")
file namefile नाम文件名os.path.basename(p)
extensionextension扩展名os.path.splitext(p)[1]
parentparent父目录os.path.dirname(p)
exists?मौजूद?存在?os.path.exists(p)
read texttext पढ़ना读文本open(p).read()
all .py belowनीचे की सारी .py下面所有 .pyos.walk + endswithos.walk + endswithos.walk + endswith
pathlib
Taskकाम任务pathlib
joinजोड़ना拼接a / "b"
file namefile नाम文件名p.name
extensionextension扩展名p.suffix
parentparent父目录p.parent
exists?मौजूद?存在?p.exists()
read texttext पढ़ना读文本p.read_text()
all .py belowनीचे की सारी .py下面所有 .pyp.rglob("*.py")
from pathlib import Path

p = Path("/home/aarav/data/report.csv")
print(p.name, p.stem, p.suffix)
print(p.parent)

new_p = Path("/home/aarav") / "reports" / "2026" / "jan.csv"
print(new_p)

# rglob: every .py file below a folder - often simpler than os.walk
for py_file in sorted(Path("project").rglob("*.py")):
    print(py_file)

# Path.walk() (Python 3.12+) works like os.walk but gives Path objects
for dirpath, dirnames, filenames in Path("project/src").walk():
    dirnames.sort()
    print(dirpath, sorted(filenames))
OUTPUT
report.csv report .csv /home/aarav/data /home/aarav/reports/2026/jan.csv project/main.py project/src/api/routes.py project/src/module.py project/src/utils.py project/src ['module.py', 'utils.py'] project/src/__pycache__ ['utils.cpython-313.pyc'] project/src/api ['README.md', 'routes.py']
🆕
Newer pathlib Featurespathlib की नई सुविधाएँpathlib 的新特性

Python 3.12 added Path.walk(), a pathlib version of os.walk. Python 3.14 added Path.copy(), Path.copy_into(), Path.move() and Path.move_into(), so copying and moving no longer need shutil. Check your Python version before using them.

Python 3.12 ने Path.walk() जोड़ा, जो os.walk का pathlib रूप है। Python 3.14 ने Path.copy(), Path.copy_into(), Path.move() और Path.move_into() जोड़े, ताकि copy और move के लिए shutil की ज़रूरत न रहे। इस्तेमाल से पहले अपना Python version जाँच लें।

Python 3.12 新增了 Path.walk(),即 pathlib 版的 os.walk。Python 3.14 新增了 Path.copy()、Path.copy_into()、Path.move() 和 Path.move_into(),复制和移动不再需要 shutil。使用前请确认你的 Python 版本。


SectionSection第 14 节

Quick Reference 📋झटपट संदर्भ 📋速查表 📋

CallCall调用What it doesक्या करता है作用Returnsलौटाता है返回
os.getcwd()current folderमौजूदा folder当前文件夹str
os.chdir(p)change current folderमौजूदा folder बदलना切换当前文件夹None
os.listdir(p)names in a folder (unordered)folder के नाम (क्रम तय नहीं)文件夹中的名字(无序)list[str]
os.scandir(p)entries with type infotype जानकारी वाली entries带类型信息的条目iterator
os.mkdir(p)one folderएक folder一个文件夹None
os.makedirs(p, exist_ok=True)folder + missing parentsfolder + ग़ायब parents文件夹 + 缺失的父目录None
os.rmdir(p)remove an empty folderख़ाली folder हटाना删除空文件夹None
shutil.rmtree(p)remove a folder and everything in itfolder और उसका सब कुछ हटाना删除文件夹及其全部内容None
os.remove(p)remove a filefile हटाना删除文件None
os.rename(a, b)rename / moveनाम बदलना / खिसकाना重命名 / 移动None
os.replace(a, b)rename, overwrite on every OSनाम बदलना, हर OS पर overwrite重命名,所有系统都覆盖None
os.stat(p)size, times, modeआकार, समय, mode大小、时间、模式stat_result
os.path.join(*parts)build a pathpath बनाना构建路径str
os.path.splitext(p)(root, ext)(root, ext)(root, ext)tuple
os.path.exists(p)does it exist?क्या मौजूद है?是否存在?bool
os.walk(top)every folder below toptop के नीचे हर foldertop 下的每个文件夹generator
os.getenv(k, default)read an env var safelyenv var सुरक्षित पढ़ना安全读取环境变量str | None
os.process_cpu_count()usable CPUs (3.13+)इस्तेमाल लायक CPUs (3.13+)可用 CPU 数(3.13+)int | None

SectionSection第 15 节

Common Pitfalls 🕳️आम ग़लतियाँ 🕳️常见陷阱 🕳️

🔗
Bare names from os.walkos.walk के खाली नामos.walk 返回的裸名字
#1 bug#1 bug头号 bug
filenames holds names only. Always os.path.join(dirpath, f) before you open, stat or delete.
filenames में सिर्फ़ नाम हैं। Open, stat या delete से पहले हमेशा os.path.join(dirpath, f) करें।
filenames 只有名字。打开、stat 或删除之前,务必 os.path.join(dirpath, f)。
✂️
Pruning with = instead of [:][:] के बजाय = से pruning用 = 而不是 [:] 剪枝
silentचुपचाप静默
dirnames = [...] changes nothing. Use dirnames[:] = [...], and only with topdown=True.
dirnames = [...] कुछ नहीं बदलता। dirnames[:] = [...] इस्तेमाल करें, और सिर्फ़ topdown=True के साथ।
dirnames = [...] 什么也不改变。请用 dirnames[:] = [...],并且只在 topdown=True 时有效。
🔀
Expecting sorted resultsSorted नतीजों की उम्मीद以为结果是排好序的
flakyअस्थिर不稳定
listdir, scandir and walk have no fixed order. Sort when order matters.
listdir, scandir और walk का कोई तय क्रम नहीं। क्रम ज़रूरी हो तो sort करें।
listdir、scandir、walk 都没有固定顺序。需要顺序时请排序。
🧵
Env vars as numbers or booleansEnv vars को numbers या booleans मानना把环境变量当数字或布尔值
typetype类型
Every value is a str. Convert with int(); compare booleans as text. bool("0") is True.
हर value str है। int() से बदलें; booleans की तुलना text के रूप में करें। bool("0") True है।
每个值都是 str。用 int() 转换;布尔值按文本比较。bool("0") 是 True。
🧨
rmtree on the wrong pathग़लत path पर rmtree对错误路径执行 rmtree
no undoकोई undo नहीं无法撤销
Print the path first, use a dry run, and never build the path from unchecked input.
पहले path छापें, dry run करें, और बिना जाँचे input से path कभी न बनाएँ।
先打印路径、先演练,永远不要用未经检查的输入构建路径。
🪟
Assuming one operating systemएक ही operating system मान लेना假设只有一种操作系统
portabilityportability可移植性
os.remove on a folder raises IsADirectoryError on Linux but PermissionError on Windows and macOS. Catch OSError to cover both.
Folder पर os.remove Linux पर IsADirectoryError देता है पर Windows और macOS पर PermissionError। दोनों पकड़ने के लिए OSError पकड़ें।
对文件夹调用 os.remove,在 Linux 上抛 IsADirectoryError,在 Windows 和 macOS 上抛 PermissionError。捕获 OSError 可同时覆盖两者。

SectionSection第 16 节

Golden Rules and Quiz 🏆सुनहरे नियम और Quiz 🏆黄金法则与小测验 🏆

🔑 The os Module — Rules to Rememberos Module — याद रखने वाले नियमos 模块 — 必须记住的规则
1
Join, don't glue. Build paths with os.path.join() or pathlib's /, never with +.
जोड़ें, चिपकाएँ नहीं। Paths os.path.join() या pathlib के / से बनाएँ, + से कभी नहीं।
拼接,不要粘贴。用 os.path.join() 或 pathlib 的 / 构建路径,绝不用 +。
2
Make folders idempotent: os.makedirs(p, exist_ok=True) is safe to run any number of times.
Folders को दोहराने-योग्य बनाएँ: os.makedirs(p, exist_ok=True) कितनी भी बार चलाना सुरक्षित है।
让建文件夹可重复执行:os.makedirs(p, exist_ok=True) 运行多少次都安全。
3
In os.walk, join and prune correctly: os.path.join(dirpath, f) for every file, dirnames[:] = … to skip folders.
os.walk में सही join और prune: हर file के लिए os.path.join(dirpath, f), folders छोड़ने के लिए dirnames[:] = …।
在 os.walk 中正确拼接和剪枝:每个文件用 os.path.join(dirpath, f),跳过文件夹用 dirnames[:] = …。
4
Sort when order matters. The file system promises no order.
क्रम ज़रूरी हो तो sort करें। File system किसी क्रम का वादा नहीं करता।
需要顺序就排序。文件系统不保证任何顺序。
5
Dry-run every bulk change. Print what will happen, read it, then flip the switch.
हर थोक बदलाव का dry run करें। छापें क्या होगा, पढ़ें, फिर switch दबाएँ।
所有批量修改先演练。打印将发生什么,看一遍,再打开开关。
6
Treat env vars as text. Use getenv with a default, convert with care.
Env vars को text मानें। Default के साथ getenv इस्तेमाल करें, ध्यान से convert करें।
把环境变量当文本。用带默认值的 getenv,小心转换。
7
Reach for pathlib in new code, and keep os for environment, processes and low-level work.
नए code में pathlib लें, और environment, processes और low-level काम के लिए os रखें।
新代码优先用 pathlib,环境、进程和底层操作仍用 os。

Quick Quiz — 8 Questionsछोटा Quiz — 8 सवाल小测验 — 8 道题

🏆 QuizPick an answer to see why.जवाब चुनें और कारण देखें।选一个答案,看看原因。
🎯
Key Takeawayमुख्य सीख关键要点

The os module is your master key to the operating system: folders, files, paths, environment variables and processes. Its most powerful tool, os.walk, hands you one folder at a time as (dirpath, dirnames, filenames). Join names with dirpath, prune with dirnames[:], sort when order matters, dry-run before you change anything — and use pathlib for new path code.

os module operating system की आपकी master key है: folders, files, paths, environment variables और processes। इसका सबसे ताक़तवर औज़ार, os.walk, एक बार में एक folder (dirpath, dirnames, filenames) के रूप में देता है। नामों को dirpath से जोड़ें, dirnames[:] से prune करें, क्रम ज़रूरी हो तो sort करें, कुछ भी बदलने से पहले dry run करें — और नए path code के लिए pathlib इस्तेमाल करें।

os 模块是你通往操作系统的万能钥匙:文件夹、文件、路径、环境变量和进程。它最强大的工具 os.walk 每次以 (dirpath, dirnames, filenames) 交给你一个文件夹。用 dirpath 拼接名字,用 dirnames[:] 剪枝,需要顺序就排序,改动之前先演练 — 新的路径代码请用 pathlib。

🐍 Try Python Yourself

Write, run and visualize Python step by step. No install needed.