Làm việc với thư mục và file sử dụng os và shutil trên Python

Post by dangsonbk on 21-03-2019

OsShutil là 2 module có sẵn của Python và được sử dụng rất phổ biến cho các công việc liên quan đến xử lý file và thư mục. Trong bài viết này, mình sẽ giới thiệu một số hàm và phương thức thường được sử dụng kèm theo ví dụ.

Trước tiên, chúng ta sẽ tìm hiểu đôi chút về việc tại sao lại tồn tại cả 2 module osshutil.

os có thể coi là một module bao gói các lệnh gọi POSIX hoặc mô phỏng chúng trên một số nền tảng mà không có POSIX. Bạn sẽ thấy các hàm gần như được đặt tên giống hệt như trong C và các ngôn ngữ khác cũng như cách thực hiện tương tự. Có thể dễ dàng nhận ra các hằng số như os.R_OK, os.WNOHANG, v.v ... được đặt tên chính xác theo C/POSIX.

shutil chứa các hàm đặc trưng Python, cho phép người dùng thực hiện nhiều thao tác phức tạp hơn so với os. Các hàm của shutil thường gọi lại nhiều hàm của os.

The shutil module offers a number of high-level operations on files and collections of files. In particular, functions are provided which support file copying and removal. For operations on individual files, see also the os module. High-level file operations

os.getcwd()

Trả về đường dẫn của thư mục đang làm việc theo dạng string.

>>> import os
>>> os.getcwd()
'd:\\Dev\\2019\\AoE'

os.listdir()

Trả về danh sách các tập tin, thư mục con trong thư mục làm việc hiện tại hoặc đường dẫn truyền vào.

>>> os.listdir()
['.cproject', '.git', '.gitignore', '.gittemplate', '.project', 'build', 'Makefile', 'src']
>>> os.listdir("d:\\Dev")
['cuccode-source', 'MasterWiki', 'mrclean', 'nevada', 'openage', 'simple-flask-vote', 'Temp']

os.walk("đường_dẫn_bắt_đầu")

Trả về danh sách toàn bộ các tập tin, thư mục con trong thư mục làm việc hiện tại và tất cả thư mục con của thư mục hiện tại, bắt đầu từ "đường_dẫn_bắt_đầu"

os.walk() sẽ trả về những thông tin sau:

Đường dẫn thư mục hiện tại. Kiểu string Tên thư mục con trong thư mục hiện tại. Kiểu list của string Tên file trong thư mục hiện tại. Kiểu list của string

Toàn bộ thông tin trên được trả về cho từng thư mục con.

Sau đây là một ví dụ sử dụng os.walk():

import os
cwd = os.getcwd()
for dir_path, dir_names, file_names in os.walk(cwd):
    for f in file_names:
        print(os.path.join(dir_path,f))

Kết quả:

./.cproject
./.gitignore
./.gittemplate
./.project
./Makefile
./src\main.cpp
./src\main.h

Vừa rồi là những lệnh giúp ta có thể lấy được thông tin của thư mục, tiếp theo là những lệnh cho phép ta thay đổi, di chuyển file ...

os.chdir("/absolute/or/relative/path")

Hàm này cho phép bạn thay đổi thư mục làm việc hiện tại sang đường dẫn được truyền vào.

os.path.join()

Module os.path chứa khá nhiều phương thức hữu ích cho các thao tác phổ biến để quản lý thư mục và file. Bạn có thể sử dụng nó để tìm thông tin về tên thư mục, các phần của tên thư mục, kiểm tra xem một tập tin hoặc thư mục tồn tại hay không ...

os.path.join() cho phép bạn có thể nối các đường dẫn với nhau theo để tạo nên 1 đường dẫn hoàn chỉnh và phù hợp nhất. Thay vì phải đau đầu sửa code để tạo đường dẫn theo từng hệ điều hành khác nhau, phương thức này sẽ giúp bạn tạo ra đường dẫn phù hợp nhất.

Lấy ví dụ, khi bạn làm việc hoặc chạy chương trình trên môi trường Unix hoặc MacOS, đường dẫn do os.path.join() tạo ra sẽ có dạng "duong_dan/den/file_hoac_folder", trong khi đó trên Windows thì đường dẫn sẽ là "duong_dan\den\file_hoac_folder". Hoặc khi bạn ghép nối 2 string: "duong_dan/" và "den/file_hoac_folder" thì kết quả sẽ là: "duong_dan//den/file_hoac_folder", trong khi sử dụng os.path.join() ta sẽ được "duong_dan/den/file_hoac_folder" một cách nhanh chóng.

Xem thêm: Why use os.path.join over string concatenation?

os.makedirs("dir1/dir2")

Đúng như tên gọi, hàn này tạo thư mục theo đường dẫn được truyền vào. Ví dụ sau đây tạo các thư mục theo 1 danh sách có sẵn:

baseDir = "D:\\Reports"
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug","Sep", "Oct", "Nov", "Dec"]
for month in months:
    os.makedir(os.path.join(baseDir,month))

shutil.copy2("source_file", "destination")

Di chuyển file từ thư mục này sang thư mục khác.

Tại sao lại là copy2? Thực ra chúng ta có nhiều hàm copy khác với những mục đích khác nhau, copy2 cho phép copy cả các thông tin về metadata, permissions. Ngoài ra, đối số destination trong copy2 có thể là đường dẫn tới thư mục.

import shutil
shutil.copy2('/src/dir/file.ext', '/dst/dir/newname.ext')       # complete target filename given
shutil.copy2('/src/file.ext', '/dst/dir')                       # target filename is /dst/dir/file.ext

Bảng dưới đây cho biết chi tiết hơn về các hàm copy khác nhau hỗ trợ bởi shutil:

┌──────────────────┬───────────────┬──────────────────┬──────────────┬───────────┐
│     Function     │Copies metadata│Copies permissions│Can use buffer│Dest dir OK│
├──────────────────┼───────────────┼──────────────────┼──────────────┼───────────┤
│shutil.copy       │      No       │        Yes       │    No        │    Yes    │
│shutil.copyfile   │      No       │        No        │    No        │    No     │
│shutil.copy2      │      Yes      │        Yes       │    No        │    Yes    │
│shutil.copyfileobj│      No       │        No        │    Yes       │    No     │
└──────────────────┴───────────────┴──────────────────┴──────────────┴───────────┘

Nguồn: stackoverflow

shutil.move("source_file", "destination")

Di chuyển file từ thư mục này sang thư mục khác. Bạn cũng có thể dùng os.rename để có kết quả tương tự.

import os
import shutil

os.rename("path/to/current/file.foo", "path/to/new/destination/for/file.foo")
shutil.move("path/to/current/file.foo", "path/to/new/destination/for/file.foo")

Lưu ý rằng trong cả hai trường hợp, thư mục chứa file mới phải tồn tại sẵn, ngoài ra thì trên môi trường Windows, trong cùng một thư mục thì không được có file và thư mục con trùng tên nhau. Bạn cũng phải chú ý tên của file (file.foo) trong cả đối số nguồn và đích nếu không tập tin sẽ bị đổi tên trong khi di chuyển.

Thường thì shutil.move sẽ gọi lại os.rename trong hầu hết các trường hợp, tuy nhiên nếu đường dẫn đích nằm trên một đĩa khác với nguồn thì hàm này sẽ không sử dụng os.rename mà sẽ sao chép file trước bằng shutil.copy2 sau đó xóa file nguồn. Nguyên nhân là os.rename sẽ chỉ làm việc khi đường dẫn nguồn và đường dẫn đích nằm trên cùng một ổ đĩa.

os.remove("my_file_path")

Xóa file ở đường dẫn được truyền vào.

Ngoài os.remove chúng ta cũng có thể sử dụng:


Tìm hiểu thêm

tags: os, shutil, python3