Hai công cụ này thường đi cùng nhau: find trả lời "những file nào thoả điều kiện?", còn xargs trả lời "làm gì đó với chúng". Nghe đơn giản, nhưng cặp đôi này là nơi ẩn một trong những cái bẫy nguy hiểm nhất của dòng lệnh — ghép chúng ẩu có thể xoá nhầm file. Bài này (phần 5 loạt Unix) chạy thật find và xargs để nắm cả sức mạnh lẫn cạm bẫy: tìm theo nhiều tiêu chí, chạy lệnh trên kết quả, và đặc biệt là cách xử lý tên file có dấu cách một cách an toàn.

find: tìm theo mọi tiêu chí

find duyệt cây thư mục và lọc theo điều kiện: tên, loại, kích thước, thời gian sửa, quyền... Vài dạng hay dùng:

find proj -name "*.go"        # theo tên (dùng glob)
find proj -type d             # chỉ thư mục (type f = file thường)
find proj -size +1k           # lớn hơn 1KB (-1k = nhỏ hơn)
find proj -mtime -7           # sửa trong 7 ngày qua
find proj -name "*.go" -exec wc -l {} \;   # chạy lệnh trên MỖI kết quả

-exec ... {} \; chạy một lệnh cho từng file tìm được ({} là chỗ điền tên file). Đây là cách find tự "làm gì đó" mà không cần xargs, nhưng chạy một tiến trình mỗi file nên chậm khi có nhiều file.

xargs: biến stdin thành đối số

Đây là chỗ nhiều người mới bối rối: vì sao ls | rm không xoá gì? Vì rm đọc tên file từ đối số dòng lệnh, không phải từ stdin. Pipe chỉ đưa dữ liệu vào stdin. xargs là cầu nối: nó đọc stdin và biến thành đối số cho lệnh:

find . -name "*.tmp" | xargs rm            # gom kết quả thành đối số cho rm
find . -name "*.log" | xargs -I{} mv {} /backup/   # {} = từng mục

Ảnh chụp đoạn mã nền tối minh hoạ find cộng xargs tìm file rồi làm gì đó với chúng an toàn, find tìm theo tên loại kích thước thời gian find proj -name sao chấm go theo tên find proj -type d chỉ thư mục f là file find proj -size cộng 1k lớn hơn 1KB find proj -mtime trừ 7 sửa trong 7 ngày qua find proj -name sao go -exec wc -l ngoặc chạy lệnh mỗi kết quả, xargs biến output stdin thành đối số cho lệnh khác vì sao ls gạch rm không chạy rm đọc đối số không đọc stdin xargs cầu nối gom dòng từ stdin thành đối số find -name sao tmp gạch xargs rm find -name sao log gạch xargs -I ngoặc mv ngoặc backup ngoặc là mỗi mục, bẫy tên có dấu cách cộng chạy song song sai xargs tách theo khoảng trắng a b tmp thành a và b tmp find -name sao tmp gạch xargs rm xoá nhầm lỗi đúng -print0 ngăn bằng NUL cộng xargs -0 find -name sao tmp -print0 gạch xargs -0 rm -P N chạy N tiến trình song song find -name sao jpg -print0 gạch xargs -0 -P4

Hình 1: find lọc theo tên/loại/kích thước/thời gian, -exec chạy lệnh mỗi kết quả; xargs biến stdin thành đối số (vì rm đọc đối số, không đọc stdin); bẫy tên có dấu cách và cờ -P chạy song song.

Đo thật: cái bẫy tên có dấu cách

Đây là phần quan trọng nhất. xargs mặc định tách đầu vào theo khoảng trắng — nên một file tên "thu muc co dau cach/rac.tmp" bị vỡ thành nhiều đối số riêng. Đo thật cho thấy rõ:

Ảnh chụp bảng kết quả chạy thật find cộng xargs output thật, một find theo loại và kích thước find proj -type d ra proj proj logs proj thu muc co dau cach proj src find proj -size cộng 1k ra proj logs app.log chỉ file lớn hơn 1KB find proj -name sao go -exec wc -l ra 1 main.go 1 util.go, hai bẫy tên thu muc co dau cach bị xargs vỡ thành 5 đối số cách sai find gạch xargs tách theo khoảng trắng 1 doi so proj thu 1 doi so muc 1 doi so co 1 doi so dau 1 doi so cach rac.tmp 1 file thành 5 mảnh rm sai, ba đúng -print0 gạch xargs -0 giữ nguyên tên có dấu cách 1 doi so proj thu muc co dau cach rac.tmp nguyên vẹn 1 doi so proj src scratch.tmp, bốn xargs -P chạy song song 6 việc sleep 0.5s tuần tự -P1 3.05 giây 6 nhân 0.5s nối đuôi song song -P6 0.51 giây chạy cùng lúc nhanh 6 lần

Hình 2: Chạy thật — find | xargs vỡ tên "thu muc co dau cach/rac.tmp" thành 5 đối số (proj/thu, muc, co, dau, cach/rac.tmp); -print0 | xargs -0 giữ nguyên tên; xargs -P6 chạy 6 việc trong 0,51s so với 3,05s tuần tự.

  • Cách sai (find | xargs): một file duy nhất biến thành 5 đối số rời rạc. Nếu lệnh là rm, nó sẽ cố xoá proj/thu, muc, co... — vừa báo lỗi vừa có nguy cơ xoá nhầm những thứ khớp các mảnh đó. Đây là lỗi kinh điển gây mất dữ liệu.
  • Cách đúng (-print0 | xargs -0): find -print0 ngăn cách các tên bằng ký tự NUL (\0) thay vì xuống dòng/khoảng trắng, và xargs -0 đọc theo NUL. Vì NUL không bao giờ xuất hiện trong tên file, mỗi tên — kể cả có dấu cách — được giữ nguyên vẹn thành đúng một đối số.

Quy tắc vàng: luôn dùng find ... -print0 | xargs -0 khi tên file có thể chứa dấu cách hay ký tự lạ. Hoặc dùng find ... -exec ... {} + (biến thể + gom nhiều file vào một lệnh, an toàn với dấu cách).

xargs -P: chạy song song

Một sức mạnh ít dùng: -P N chạy N tiến trình song song. Đo thật với 6 việc, mỗi việc sleep 0.5s:

  • -P1 (tuần tự): 3,05 giây — 6 việc nối đuôi nhau.
  • -P6 (song song): 0,51 giây — cả 6 chạy cùng lúc, nhanh gần 6 lần.

Với các tác vụ độc lập tốn thời gian (resize ảnh, nén file, tải nhiều URL), xargs -P là cách đơn giản nhất để dùng hết nhiều lõi CPU mà không cần viết code song song.

Đánh đổi cần cân nhắc

-exec {} \; vs -exec {} + vs xargs. -exec ... \; chạy một tiến trình mỗi file (chậm nhưng đơn giản); -exec ... + gom nhiều file vào một lần gọi (nhanh, an toàn dấu cách); xargs linh hoạt nhất (song song, điều khiển batch). Với thao tác đơn giản an toàn, -exec + gọn; cần song song thì xargs -P.

Song song không phải lúc nào cũng nhanh hơn. -P chỉ lợi khi tác vụ độc lập và bị chặn bởi CPU/mạng, không phải bởi đĩa. Với tác vụ nặng I/O trên một ổ đĩa, chạy nhiều tiến trình cùng lúc có thể chậm hơn do tranh đĩa. Và đừng đặt -P quá cao — quá số lõi thường không thêm lợi mà tốn bộ nhớ.

Kiểm trước khi xoá. Với lệnh phá huỷ (rm, mv đè), luôn chạy find ... -print0 | xargs -0 echo (hoặc ls) trước để xem danh sách sẽ bị tác động, rồi mới đổi echo thành rm. Một mẫu find sai kết hợp xargs rm có thể quét sạch nhầm thư mục trong tích tắc — không có thùng rác ở dòng lệnh.

Ba ý mang về

  1. find lọc, xargs biến kết quả thành đối số: find tìm theo tên/loại/kích thước/thời gian (-exec chạy lệnh mỗi kết quả); xargs cần thiết vì nhiều lệnh (rm) đọc đối số chứ không đọc stdin — nên ls | rm không chạy.
  2. Luôn -print0 | xargs -0 với tên có dấu cách: đo thật, find | xargs vỡ một tên có dấu cách thành 5 đối số (nguy cơ xoá nhầm), còn -print0/-0 ngăn bằng NUL giữ tên nguyên vẹn. Hoặc dùng -exec {} +.
  3. xargs -P chạy song song gần như miễn phí: đo thật 6 việc chạy -P6 trong 0,51s so với 3,05s tuần tự (~6×) — hợp cho tác vụ độc lập; nhưng luôn thử bằng echo trước khi ghép với lệnh phá huỷ.

Nguồn

Phần sau ta xem bộ ba xử lý văn bản theo dòng: sort, uniq, cut — vì sao uniq chỉ gộp dòng trùng liền kề (nên gần như luôn phải sort trước), và cách đếm tần suất bằng sort | uniq -c.