Tại sao trên Linux hay dùng những định dạng file nén như *.gz, *.xz thay vì *.zip, *.7z?

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*.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:

$ 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:

$ 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

Nếu bạn muốn độ nén mạnh như 7zip thì bạn thay gzip bằng xz:

$ 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_dumppsql 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.

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:

$ 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:

$ ssh old-laptop "tar -cO -C ~/Works folder-to-copy | gzip" | gunzip | tar -xf-

hay ngắn gọn hơn:

$ 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:

$ ssh old-laptop "tar -cO -C ~/Works folder-to-copy" | pv | tar -xf-

pv

(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 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.