Lệnh watch trong Linux: Cách dùng đúng và đủ
Lệnh watch chạy lại command mỗi 2 giây. Học cách chọn interval, highlight diff, thoát khi có thay đổi và quote pipe để watch chạy đúng.
Lệnh watch làm gì
Lệnh watch chạy lại một lệnh khác theo khoảng thời gian cố định và vẽ lại output tại cùng vị trí trên màn hình. Khoảng thời gian mặc định là 2 giây. Hãy dùng lệnh này khi bạn đang chờ một giá trị thay đổi: dung lượng disk tăng dần, container ổn định, chứng chỉ được gia hạn hoặc queue được xử lý hết.
watch thuộc procps-ng và đã được cài sẵn trên VPS Ubuntu hoặc Debian mới, trong package procps. Hãy kiểm tra version trước khi dùng các flag bên dưới, vì 2 tùy chọn hữu ích trong số đó mới được bổ sung gần đây.
watch --versionTrên Ubuntu 24.04, lệnh này in ra một dòng tương tự watch from procps-ng 4.0.4. Sau đây là ví dụ đơn giản và hữu ích nhất.
watch df -h /Màn hình được xóa và hiển thị một dòng header, một dòng trống, rồi output của df -h /:
Every 2.0s: df -h / server1: Sun Aug 9 10:21:44 2026Bên trái là khoảng thời gian và lệnh chính xác mà watch đang chạy. Bên phải là hostname và thời gian hiện tại. Đồng hồ là phần hữu ích vì nó xác nhận màn hình vẫn đang refresh dù các giá trị bên dưới không thay đổi. Nhấn Ctrl+C để thoát.
watch vẽ một frame vừa với terminal. Các dòng dài sẽ bị wrap, còn -w sẽ truncate chúng; mọi nội dung vượt quá dòng cuối cùng sẽ không được vẽ. Trong frame không có scrollback vì mỗi chu kỳ ghi đè lên chu kỳ trước. Chính đặc điểm này quyết định phần lớn những việc watch phù hợp để theo dõi.
Tần suất watch chạy lại lệnh nên là bao lâu?
Hai giây chỉ là giá trị mặc định. Chọn khoảng thời gian dựa trên tốc độ thay đổi của giá trị và chi phí mỗi lần chạy.
-n 0.5cho các bộ đếm thay đổi liên tục, chẳng hạn nhưss -shoặc số byte trên một interface. Khoảng thời gian nhỏ nhất mà watch chấp nhận là 0.1 giây.-n 30hoặc-n 60cho disk và certificate. Một filesystem đầy lên trong một ngày không cần hiển thị ba mươi khung hình mỗi phút.
watch chạy lệnh, đợi lệnh hoàn tất rồi mới sleep trong khoảng thời gian đã đặt. Thời gian chạy được cộng vào khoảng cách giữa các lần chạy. Vì vậy, một lệnh mất tám giây khi dùng -n 2 sẽ cho bạn một khung hình mỗi mười giây và giữ máy bận trong toàn bộ thời gian đó. Thêm -p (--precise), watch sẽ cố gắng bắt đầu mỗi lần chạy sau đúng số giây đã đặt, tính từ lúc bắt đầu lần chạy này đến lúc bắt đầu lần chạy tiếp theo.
Nếu luôn muốn dùng một giá trị mặc định khác, hãy export WATCH_INTERVAL trong shell profile. Một -n được chỉ định rõ ràng vẫn được ưu tiên hơn biến này. Chạy watch --help để xác nhận bản build của bạn đọc biến đó.
Đánh dấu thay đổi bằng -d
Đọc một màn hình toàn chữ giống nhau để tìm một trường duy nhất vừa thay đổi là việc khó. -d (--differences) làm việc đó thay bạn.
watch -n 30 -d 'df -h / /var'Các ký tự khác với frame trước được hiển thị bằng video đảo, vì vậy ô Use% sáng lên ngay khi giá trị thay đổi, còn mọi thứ khác vẫn giữ nguyên. Nếu /var không phải là một filesystem riêng, bạn sẽ thấy root filesystem được liệt kê hai lần. Đây là cách nhanh để biết máy được phân vùng như thế nào.
Việc so sánh dựa trên vị trí. Điều đó có nghĩa là watch đối chiếu từng dòng của frame mới với frame cũ, theo từng ký tự. Output có các cột thay đổi độ rộng sẽ sáng gần như toàn bộ, vì vậy nên ưu tiên các command có layout ổn định. procps-ng hiện cũng chấp nhận watch --differences=permanent, tùy chọn này giữ highlight tại mọi vị trí từng thay đổi thay vì chỉ highlight thay đổi gần nhất. Hãy xác nhận bằng watch --help trước khi dựa vào tùy chọn này.
Làm thế nào để watch thoát khi output thay đổi?
-g (--chgexit) khiến watch dừng ngay lần đầu output khác với lần chạy trước. Prompt quay lại, nên bạn có thể đặt lệnh khác phía sau trên cùng một dòng.
watch -n 10 -g 'systemctl is-active myapp' ; echo 'state changed'Đây là flag biến watch thành công cụ “báo cho tôi khi deploy hoàn tất”. Flag này có một bẫy mà ai cũng dễ gặp: phép so sánh áp dụng cho toàn bộ output, nên mọi giá trị thời gian hoặc PID (process identifier) trong đó sẽ thay đổi ngay ở lần refresh đầu tiên và watch thoát lập tức. systemctl status myapp in thời gian đã trôi qua và mức sử dụng memory, nên dùng -g với lệnh này là vô ích. Hãy rút gọn command còn đúng một thông tin ổn định mà bạn đang chờ. Đó là tác dụng của systemctl is-active.
Các bản procps-ng mới hơn bổ sung phép kiểm tra ngược lại, --equexit <cycles>. Flag này khiến watch thoát sau khi output giữ nguyên trong số chu kỳ đó. Nó trả lời câu hỏi “báo cho tôi khi hoạt động này đã im”. Hãy kiểm tra watch --help, vì các bản build cũ không có flag này.
Vì sao watch bỏ qua pipe và glob của tôi?
Đây là lỗi mà hầu như ai cũng gặp một lần. Nguyên nhân nằm ở shell chứ không phải ở watch.
watch docker compose ps | grep webShell tương tác đọc toàn bộ dòng lệnh trước khi chạy bất kỳ thứ gì, rồi tách dòng đó tại pipe. Nó khởi chạy watch docker compose ps và nối output màn hình của chính watch vào grep. Lúc này watch không còn ghi ra terminal, nên màn hình hiển thị bị hỏng hoặc trống, còn grep đang lọc output của các lần redraw thay vì danh sách container.
Hãy quote toàn bộ pipeline để truyền nó dưới dạng một argument.
watch 'docker compose ps | grep web'watch truyền chuỗi đó cho sh -c, rồi shell đó chạy pipeline một lần trong mỗi cycle. Glob cũng tuân theo quy tắc này, nhưng biểu hiện kín đáo hơn.
watch ls -l /var/log/*.log
watch 'ls -l /var/log/*.log'Dòng đầu tiên mở rộng glob một lần trong shell của bạn, ngay khi bạn nhấn Enter. Sau đó watch chạy lại một danh sách tên file cố định, nên file log được tạo một phút sau sẽ không xuất hiện. Dòng thứ hai truyền glob cho watch. watch mở rộng glob bên trong sh ở mỗi cycle, nên các file mới tự động xuất hiện.
Quote cũng quyết định thời điểm đánh giá biến hoặc command substitution. watch "echo $(date)" chạy date một lần trong shell của bạn, rồi echo chuỗi cố định đó mãi mãi. watch 'echo $(date)' chạy date ở mỗi cycle. Single quote có nghĩa là đánh giá sau. Double quote có nghĩa là đánh giá ngay.
Cách sh -c này còn có 2 hệ quả:
- Alias và shell function không tồn tại bên trong nó.
watch llthất bại vớish: 1: ll: not foundvìsh -ckhông đọc.bashrccủa bạn. Hãy ghi trực tiếp command thật, hoặc dùng full path đến binary chỉ có trongPATHtương tác của bạn. - Nên capture standard error. Đặt
2>&1bên trong quote khi bạn muốn phần text lỗi được hiển thị trong frame thay vì rải ra bên ngoài.
Khi việc quote trở nên khó xử, -x (--exec) chạy command trực tiếp thay vì thông qua sh -c. Argument có khoảng trắng sẽ dễ xử lý hơn, nhưng pipe và glob sẽ hoàn toàn không hoạt động vì không còn shell để diễn giải chúng.
Quyền cũng tuân theo logic này vì watch là một process thông thường. watch 'sudo ss -tulpn' sẽ hiển thị password prompt mà bạn không thể thấy hoặc nhập vào. Thay vào đó, hãy chạy sudo watch 'ss -tulpn' và nhớ rằng toàn bộ loop sau đó sẽ chạy dưới quyền root cho đến khi bạn nhấn Ctrl+C.
Bốn thứ đáng theo dõi trên VPS
Một disk đang đầy dần
watch -n 30 -d 'df -h / /var'df yêu cầu kernel cung cấp các số liệu đã có sẵn, nên đủ nhẹ để chạy lặp vô hạn. du -sh /var/log thì không, vì mỗi lần chạy nó phải duyệt mọi file trong cây thư mục. Khi đặt dưới watch, lệnh này liên tục đọc disk và làm chậm chính máy bạn đang chẩn đoán. Chạy du một lần, hoặc dùng ncdu, rồi để df dưới watch để biết số liệu có tiếp tục tăng hay không.
Một container liên tục restart
cd /srv/myapp
watch -n 5 -d 'docker compose ps'Cột trạng thái cho biết mỗi container đã chạy được bao lâu. Container rơi vào crash loop sẽ liên tục reset về trạng thái "Up 2 seconds", và -d làm điều đó hiện rõ ngay lập tức. Hãy đổi thư mục trước, vì watch kế thừa working directory của shell đã khởi chạy nó, còn docker compose cần project directory của ứng dụng. watch cho biết restart đang xảy ra. docker compose logs -f web cho biết nguyên nhân. Khi đã biết nguyên nhân, hãy chuyển cách xử lý đó thành một Compose healthcheck để tự restart container để không ai phải ngồi nhìn màn hình.
Một lần gia hạn certificate sắp hoàn tất
sudo -i
watch -n 60 -g 'openssl x509 -noout -enddate -in /etc/letsencrypt/live/example.com/fullchain.pem'Lệnh này chỉ in một dòng, notAfter=Nov 7 09:14:22 2026 GMT, và không in gì khác, nên -g chỉ chạy đúng lúc file được thay thế bằng bản đã gia hạn. Các file bên dưới /etc/letsencrypt/live chỉ root mới đọc được, nên lệnh này được chạy trong root shell. Lưu ý rằng lệnh theo dõi certificate trên disk. Web server đang chạy vẫn giữ certificate cũ trong memory cho đến khi reload, nên hãy dùng echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -enddate để kiểm tra certificate thực sự đang được phục vụ, và không chạy lệnh đó với interval hai giây trên một public host. Việc gia hạn thuộc về một systemd service và timer, còn systemctl list-timers cho biết lần chạy tiếp theo.
Một queue đang được xử lý hết dần
watch -n 15 'postqueue -p | tail -n 1'Lệnh này in một dòng tóm tắt như -- 24 Kbytes in 6 Requests., hoặc Mail queue is empty sau khi xử lý xong. Cùng cách này áp dụng được cho mọi queue mà bạn có thể đếm: watch -n 5 'find /srv/queue/incoming -type f | wc -l'.
Đây là giới hạn của watch. Nó so sánh một frame với frame ngay trước đó và không thể kiểm tra điều kiện, nên không bao giờ tự nói được rằng "số lượng đã về 0". Một shell loop thông thường có thể làm việc đó bằng cách dùng command substitution để lấy số lượng ở mỗi lần chạy:
until [ "$(find /srv/queue/incoming -type f | wc -l)" -eq 0 ]; do sleep 5; done; echo 'queue drained'Khi watch không phải công cụ phù hợp
watch chạy lại command mà không lưu trạng thái của lần chạy trước và không biết command đó thực hiện việc gì. Điều này phù hợp với df. Nhưng watch không phù hợp trong 4 trường hợp.
- Log. watch vẽ lại một khung cố định, nên các dòng xuất hiện rồi cuộn khỏi màn hình giữa hai chu kỳ sẽ bị mất. Hãy dùng
journalctl -fu nginxhoặctail -fđể stream các dòng mới ngay khi chúng được ghi. - Command tốn tài nguyên.
du -sh /hoặc query đến một database đang bận sẽ trở thành tải nền liên tục ở bất kỳ khoảng thời gian nào, vì watch liên tục khởi chạy lại command đó. - Bất kỳ thao tác nào có side effect. Command thực hiện ghi dữ liệu, gửi request, restart hoặc install sẽ lặp lại ở mỗi chu kỳ. Một
curlđến API mỗi 2 giây tương đương 43,200 request mỗi ngày. Đây là cách khiến free API key bị suspended. - Kiểm tra chạy dài hạn. watch chạy trong terminal và dừng khi session của bạn kết thúc. Nó không alert cho ai và không lưu history sau khi bạn đóng laptop. Với trường hợp này, bạn cần một status monitor thực sự như Uptime Kuma, hoặc một check được schedule và chạy trên server.
Ranh giới thực tế nằm ở thời gian. watch phù hợp cho 10 phút bạn chờ một việc cụ thể xảy ra. Bất kỳ việc gì vẫn cần được kiểm tra vào ngày mai đều nên dùng scheduled timer hoặc một monitor thực sự.
Chạy watch trong tmux khi dùng SSH
Qua SSH, watch chỉ bền chừng nào kết nối còn hoạt động. Khi đường truyền bị ngắt, shell nhận SIGHUP và dừng watch theo. tmux giải quyết vấn đề này, nhờ đó watch thực sự hữu ích trên máy chủ từ xa.
tmux new -As ops
watch -n 30 -d 'df -h /'Nhấn Ctrl+b rồi d để detach. Vòng lặp vẫn tiếp tục chạy trên server. Kết nối lại sau bằng tmux attach -t ops và khung hình vẫn được cập nhật; đồng hồ trong header xác nhận điều đó. Chia cửa sổ để giữ watch ở một bên và journalctl -f ở bên kia. Đây chính là ý tưởng của một terminal workbench dựa trên tmux. Khi cần đặt cùng một câu hỏi trên nhiều máy cùng lúc, hãy dùng một công cụ được thiết kế để quản lý nhiều Linux server thay vì tạo một bức tường pane chạy watch.
Các màn hình được trích dẫn ở đây chỉ mang tính minh họa. Bố cục cột và chuỗi phiên bản khác nhau giữa các distribution và bản phát hành procps-ng. Vì vậy, hãy đọc man watch trên máy của bạn để biết các flag mà bản build thực tế hỗ trợ.
FAQ
Vì sao watch bỏ qua pipe trong watch mycmd | grep foo?
Shell của bạn tách dòng lệnh tại pipe trước khi watch khởi chạy. Vì vậy, shell chuyển phần hiển thị của watch vào grep, còn output của lệnh không được lọc. Hãy đặt toàn bộ pipeline trong dấu quote: watch 'mycmd | grep foo'. watch truyền một argument duy nhất đó cho sh -c, rồi shell này chạy pipeline một lần trong mỗi chu kỳ. Quy tắc tương tự áp dụng cho glob và command substitution.
Tôi nên dùng interval nào với watch?
Chọn interval dựa trên tốc độ thay đổi của giá trị và chi phí của mỗi lần chạy. Nửa giây phù hợp với các bộ đếm thay đổi nhanh. 0.1 giây là interval nhỏ nhất mà watch chấp nhận. Với disk và certificate, 30 đến 60 giây thường phù hợp. Runtime của lệnh được cộng thêm vào khoảng chờ, trừ khi bạn truyền -p. Vì vậy, một lệnh chạy chậm với -n 1 sẽ chạy liên tiếp và không bao giờ tạm dừng.
watch có thể theo dõi một file log không?
Không. watch vẽ lại một frame và không lưu history. Vì vậy, mọi dòng xuất hiện rồi cuộn khỏi màn hình giữa hai chu kỳ sẽ mất vĩnh viễn. journalctl -fu <unit> và tail -f stream các dòng mới ngay khi chúng xuất hiện, phù hợp với log. Chỉ dùng watch cho các giá trị có trạng thái hiện tại, chẳng hạn phần trăm disk hoặc độ dài queue.
Làm cách nào để watch dừng khi điều tôi đang chờ xảy ra?
Dùng -g (--chgexit) và truyền cho nó một lệnh có output giữ nguyên cho đến khi sự kiện đó xảy ra. Output chứa clock hoặc PID sẽ thay đổi ngay lần refresh đầu tiên và khiến lệnh thoát ngay. Vì vậy, systemctl is-active myapp hoạt động trong trường hợp systemctl status myapp không phù hợp. Với điều kiện thực tế như một count giảm xuống 0, hãy dùng shell until loop, vì watch chỉ có thể phát hiện output đã thay đổi.
Vì sao watch báo command not found với một lệnh vẫn chạy được trong shell của tôi?
watch truyền lệnh cho sh -c. Shell đó không đọc .bashrc, nên các alias và shell function của bạn không tồn tại trong đó. Thông báo này thường là sh: 1: ll: not found. Hãy ghi rõ lệnh thật hoặc dùng full path cho binary chỉ có trong PATH interactive của bạn.