--- title: Tại sao trên Linux hay dùng những định dạng file nén như *.gz, *.xz thay vì *.zip, *.7z? date: 2023-03-21 19:27:37.685487 UTC --- Những bạn đang dùng Windows mà tìm hiểu về Linux sẽ thấy trên Linux, những định dạng file nén quen thuộc như _*.zip_, _*.7z_ lại không được ưa chuộng, mà lại thấy người ta hay sử dụng _*.gz_, _*.tar.gz_, _*.zz_, _*.tar.xz_, mặc dù cả định dạng và thuật toán của zip, 7zip đều là mã nguồn mở. Thế thì tại sao? Đầu tiên, hãy nói về một định dạng file nén khác cực kì được ưa chuộng trên Windows là _*.rar_. Lý do định dạng này vắng mặt trên Linux thì dễ hiểu hơn, đó là nó không phải là phần mềm nguồn mở. Quay lại _*.zip_ và _*.7z_. Lý do chúng không được ưa chuộng là vì chúng không hỗ trợ nén và giải nén theo kiểu "cuốn chiếu". Theo kiểu "cuốn chiếu" nghĩa là, ví dụ như bạn đang download một file nén lớn và bạn muốn download tới đâu, giải nén tới đó, không phải chờ download xong xuôi rồi mới giải nén. Tính năng "cuốn chiếu" này quan trọng đối với người dùng Linux vì trên Linux, môi trường dòng lệnh rất mạnh, có một tính năng gọi là "pipe" và tính "cuốn chiếu" kết hợp với "pipe" thì giúp công việc hiệu quả gấp nhiều lần. Ví dụ, tôi hay sử dụng "pipe" (truyền dữ liệu đầu ra của chương trình này vào đầu vào của chương trình khác) khi tôi muốn copy cơ sở dữ liệu từ server về máy cá nhân: ```console $ ssh my-server 'pg_dump -O database_name' | psql database_name ``` Câu lệnh trên có nghĩa là: - SSH vào server _my-server_, chạy lệnh `pg_dump` trên đó để dump dữ liệu PostgreSQL. Tuy nhiên kết quả dump không lưu vào file mà đẩy ra _standard output_ (_stdout_). Khi bạn chạy lệnh `pg_dump` trong `ssh` thì _stdout_ của `pg_dump` sẽ được ngầm download về, nhả ra qua _stdout_ của `ssh` luôn. - Cùng lúc đó, chạy lệnh `psql` trên máy cá nhân để khôi phục dữ liệu đã dump. - Toán tử pipe (`|`) giúp kết nối _stdout_ của `ssh` với _standard input_ (_stdin_) của `psql`. Như vậy kết quả dump của `pg_dump` sẽ đi thẳng vào `psql` mà không cần ra một file trung gian. Trong trường hợp dữ liệu hơi lớn mà đường truyền Internet không quá nhanh thì tôi sẽ nén kết quả dump trước rồi mới gửi về máy cá nhân, bằng cách chèn thêm `gzip` vào: ```console $ ssh my-server 'pg_dump -O database_name | gzip' | gunzip | psql database_name ``` Câu lệnh trên có nghĩa là: - SSH vào server _my-server_, chạy lệnh `pg_dump` trên đó để dump dữ liệu PostgreSQL. Kết quả dump không lưu vào file mà đẩy ngay sang cho `gzip` để nén. Đầu ra đã nén sẽ được download và đi ra _stdout_ của lệnh `ssh`. - Kết quả nén download được tới đâu, sẽ truyền ngay qua `gunzip` tới đó để giải nén. - Kết quả giải nén lại được truyền ngay vào `psql` để khôi phục dữ liệu mà không cần qua file trung gian. ![dump_db_and_download](https://quan-images.b-cdn.net/blogs/imgur/2026/t8ShO2T.png) Nếu bạn muốn độ nén mạnh như 7zip thì bạn thay gzip bằng xz: ```console $ ssh my-server 'pg_dump -O database_name | xz' | unxz | psql database_name ``` Tất cả các ứng dụng trên, từ `ssh` đến `psql` đều xử lý dữ liệu theo kiểu "cuốn chiếu" nên kết hợp với "pipe" là tuyệt vời. Nếu bạn thắc mắc tại sao có thể dùng lệnh `pg_dump` và `psql` một cách ngắn gọn, không cần cung cấp host, username, password, thì đọc thêm [bài này](https://quan.hoabinh.vn/blog/2015/10/22-truy-cap-nhanh-giao-dien-dong-lenh-cua-postgresql). Vì mục tiêu hỗ trợ nén & giải nén cả stream (luồng dữ liệu đang chảy) nên _gzip_, _xz_ chỉ có thể làm việc với file đơn lẻ, không thể làm việc với cả thư mục như zip, 7zip. Để nén & giải nén thư mục thì người ta kết hợp với `tar`, một lệnh để đóng gói thư mục thành một file. Điều thú vị là `tar` cũng đóng gói và mở gói thư mục theo kiểu "cuốn chiếu". Sau đây là một ví dụ khi tôi mua laptop mới và cần sao chép dữ liệu công việc từ laptop cũ sang laptop mới. Do dữ liệu rất lớn (hàng chục GiB), gồm hàng trăm ngàn file nhỏ nên không thể copy bằng phím Ctrl+C/Ctrl+V trong ứng dụng giao diện đồ họa được, do khi copy bằng cách này thì ứng dụng sẽ copy từng file một, sẽ cực kỳ lâu. Nén cả thư mục trước khi copy thì sẽ nhanh hơn, nhưng nếu dùng `zip`, `7zip` thì sẽ gặp trở ngại là nó phải tạo ra file trung gian, trong khi ổ cứng không đủ để lưu file này, chưa kể sẽ phải chờ đợi file sinh ra trọn vẹn rồi mới copy và giải nén được. Trong trường hợp này, tôi kết hợp `ssh`, `tar` để không phải sinh ra file trung gian: ```console $ ssh old-laptop "tar -cO -C ~/Works folder-to-copy" | tar -xf- ``` Giải thích: - Cờ `-c` trong lệnh `tar` để yêu cầu `tar` thi hành đóng gói, `-x` là để mở gói. - `-O` để yêu cầu tar đẩy kết quả đóng gói ra _stdout_. - `-C` để yêu cầu `tar` nhảy đến thư mục cha nào đó trước khi đóng gói thư mục con. - `-f -` (trong lệnh `tar` thứ hai) để hướng dẫn lấy file đã đóng gói (đầu vào) từ _standard input_. Ở đây, tôi dùng mạng dây nội bộ nên tốc độ truyền đủ nhanh để không cần nén, tuy nhiên nếu muốn nén thì có thể kết hợp với gzip: ```console $ ssh old-laptop "tar -cO -C ~/Works folder-to-copy | gzip" | gunzip | tar -xf- ``` hay ngắn gọn hơn: ```console $ ssh old-laptop "tar -czO -C ~/Works folder-to-copy" | tar -xzf- ``` Thêm một mẹo nhỏ, nếu bạn muốn theo dõi tiến độ của việc chuyển dữ liệu trên, bạn có thể kết hợp với `pv`: ```console $ ssh old-laptop "tar -cO -C ~/Works folder-to-copy" | pv | tar -xf- ``` ![pv](https://quan-images.b-cdn.net/blogs/imgur/2026/kE8j76z.png) (Trong hình trên, tôi đang minh họa copy dữ liệu ở nơi có mạng chậm nên tốc độ hơi chậm). Như vậy, trong bài này, không những bạn hiểu được lý do tại sao `gzip`, `xz` lại được ưa dùng hơn `zip`, `7zip` mà còn được thấy sức mạnh của môi trường dòng lệnh trên Linux, ích lợi của môi trường dòng lệnh đối với năng suất công việc. -- Cập nhật -- - Hiện nay tôi chuyển qua dùng [*lzip*](https://www.nongnu.org/lzip/) thay cho *xz* vì cũng cùng thuật toán nén nhưng *lzip* được implement tốt hơn, tránh lỗi tốt hơn. Khi tạo gói *tar* với *lzip* thì nên dùng [*tarlz*](https://www.nongnu.org/lzip/tarlz.html).