Chạy một lệnh dài, xong thì để Mac ngủ lại

Muốn chạy một lệnh rồi để Mac ngủ lại ngay khi lệnh xong, công cụ có sẵn là caffeinate: đưa lệnh của bạn vào làm tham số (caffeinate -i your-command), macOS sẽ giữ Mac thức đúng trong vòng đời của tiến trình đó, rồi tự buông ra — không cần bước bật/tắt riêng. Cái này thực sự hữu ích và hoàn toàn miễn phí. Chỗ nó hết tác dụng là mọi thứ sau khi lệnh kết thúc: caffeinate không báo cho bạn biết job có chạy thành công không, không theo dõi pin trong lúc chạy, và không thông báo khi xong việc. Run & Watch a Command của LidRun (và người anh em CLI của nó, lidrun --) làm đúng việc gắn vòng đời như vậy, rồi thêm vào exit code, thời gian chạy, thông báo, và một cơ chế giám sát an toàn thực sự dừng job lại nếu Mac không nên tiếp tục chạy nó.
Vấn đề của keep-awake thủ công
Quy trình thường thấy là hai bước bạn phải nhớ đúng thứ tự: bật keep-awake, chạy job, rồi sau đó — hy vọng là vậy — tắt keep-awake khi xong. Bước thứ hai mới là bước hay bị quên.


Quên tắt thì Mac cứ thức hàng giờ sau khi job bốn mươi phút đã xong từ lâu — hao pin, hoặc chỉ đơn giản là không được nghỉ trên bàn. Tắt sớm quá thì lại cắt ngang job giữa chừng. Cả hai đều không phải điều bạn thực sự muốn, mà thứ bạn muốn đơn giản hơn nhiều so với những gì các nút bấm cho phép: giữ Mac thức trong lúc lệnh này chạy, rồi dừng lại. Gắn trạng thái keep-awake vào chính vòng đời của công việc, chứ không phải vào một cái công tắc do tay người bật tắt.
Cách miễn phí có sẵn: caffeinate
Trước khi tìm đến bất kỳ app nào, nên biết là macOS đã có sẵn miễn phí đúng cái mẹo cốt lõi này. caffeinate có sẵn trên mọi Mac, và theo đúng man page của nó: "Nếu có chỉ định một utility, caffeinate sẽ tạo assertion thay cho utility đó, và assertion sẽ tồn tại trong suốt thời gian utility chạy." Ví dụ chính thức của nó cũng đúng là pattern này: caffeinate -i make fork ra tiến trình make, giữ một idle-sleep assertion trong suốt thời gian nó chạy, rồi giải phóng khi nó kết thúc. Đổi sang job của riêng bạn — caffeinate -i ./run-migrations.sh hoặc caffeinate -i npm test — là bạn có ngay kiểu keep-awake bám theo đúng thời lượng lệnh mà LidRun cung cấp, không cần cài đặt gì thêm.
Các flag ở đây quan trọng: -i ngăn idle sleep (trường hợp phổ biến nhất), -d thì giữ luôn cả màn hình sáng, -s chặn system sleep hoàn toàn nhưng chỉ hoạt động khi cắm sạc, còn -m giữ ổ đĩa không bị quay chậm lại. Bỏ hết flag đi thì caffeinate your-command trơn vẫn mặc định ngăn idle sleep. Đây thực sự là một công cụ chắc chắn cho một job chạy foreground mà bạn đang theo dõi.
Cái mà caffeinate không đụng tới là nắp đậy. Assertion của nó chỉ chặn idle sleep, không chặn được forced sleep mà macOS kích hoạt ngay khi bạn đóng nắp máy trên một Mac không cắm màn hình ngoài. Cách khắc phục miễn phí truyền thống ở đây là setup clamshell kinh điển — màn hình ngoài cộng bàn phím/chuột ngoài, đóng nắp lại, Mac vẫn chạy vì macOS coi như nó đang ở chế độ dock. Đòn bẩy còn lại, cũng chính là thứ mà Closed-Lid mode của LidRun gọi ngầm bên dưới, là lệnh sudo pmset disablesleep 1 không được tài liệu hóa chính thức nhưng ai cũng biết — một flag toàn hệ thống chặn sleep hoàn toàn cho tới khi có gì đó đặt nó về lại 0.
Chỗ cách miễn phí hết tác dụng
caffeinate không báo gì cho bạn khi job kết thúc. Không có exit code được in ra, không có thời gian chạy, không có thông báo — bạn lại quay về cảnh ngồi canh tab terminal, hoặc tự viết wrapper riêng kiểu caffeinate -i ./job.sh; echo "exit $?" rồi nhớ quay lại kiểm tra sau. Với một migration bốn mươi phút thì đây chỉ là phiền toái nhỏ; còn với một export chạy qua đêm bạn bật trước khi đi ngủ, nó nghĩa là bạn thức dậy phải cuộn xem cửa sổ terminal, chứ không có câu trả lời rõ ràng nào chờ sẵn.


caffeinate cũng chẳng biết gì về pin hay trạng thái nhiệt của Mac. Nó giữ assertion một cách mù quáng suốt thời gian tiến trình chạy — đúng như bạn yêu cầu, nhưng nghĩa là một job caffeinate -i chạy qua đêm trên pin mà không ai để mắt tới thì cũng chẳng có ai canh cái ngưỡng pin thấp bạn đã cấu hình thay bạn cả. pmset disablesleep còn thô hơn: nó cần sudo, nó áp dụng cho toàn hệ thống (chặn sleep cho mọi thứ, không riêng job của bạn), và nó vẫn giữ ở 1 cho tới khi có gì đó chủ động đặt lại — bỏ qua bước đó thì Mac sẽ không ngủ vì bất cứ lý do gì cho tới khi bạn nhớ ra.
Các app bật/tắt của bên thứ ba — Amphetamine, KeepingYouAwake, Lungo, Caffeine bản gốc — đáng được nhắc tới công bằng chứ không phải để gạt bỏ. Amphetamine đặc biệt có một hệ thống trigger phong phú: có thể kích hoạt khi mở app, theo lịch, hoặc theo mức pin — cấu hình được nhiều hơn bất cứ thứ gì LidRun cung cấp ở đây. KeepingYouAwake và Lungo là những công tắc menu bar tối giản có chủ đích; Caffeine là bản kinh điển một-cú-click. Điều không app nào trong số đó làm là theo dõi exit code của một lệnh cụ thể — chúng quản lý một trạng thái thức kéo dài mà bạn tự bật tự tắt, hoặc theo một trigger chẳng liên quan gì tới việc job của bạn có còn chạy thật hay không. Đó vẫn là đúng vấn đề quên-tắt ở phần đầu, chỉ là khoác thêm cái icon menu bar đẹp hơn.
Giao lệnh cho LidRun thay vì tự làm
Run & Watch a Command là một tính năng Pro lật ngược mô hình của caffeinate: thay vì bạn tự quản lý trạng thái keep-awake, bạn giao lệnh cho LidRun và nó quản lý trạng thái đó thay bạn. Bên dưới, nó chạy lệnh của bạn qua đúng login shell mà Terminal dùng (/bin/zsh -l -c), nên các công cụ nằm trong PATH — Homebrew, pyenv, nvm, bất cứ thứ gì claude hay ollama cần để resolve — vẫn hoạt động y như khi bạn tự gõ lệnh. Nó giữ keep-awake suốt vòng đời của lệnh và giải phóng ngay khi lệnh kết thúc — đúng lời hứa mà LidRun đưa ra ở mọi nơi khác trong app: lệnh còn chạy → giữ thức, lệnh xong hoặc không an toàn → giải phóng.
Trong lúc chạy, LidRun giữ lại phần đuôi output cuộn liên tục — 200 dòng gần nhất, hiển thị trực tiếp trong cửa sổ menu bar — để bạn không phải nhìn chằm chằm một terminal trơn và tự hỏi nó còn sống không. Đây là view trực tiếp, không phải file log được lưu lại, nên nếu cần bản ghi đầy đủ để xem sau, hãy redirect output của lệnh ra file như bạn vẫn thường làm. Nếu bạn gõ một interpreter trơn mà không có gì để chạy thật (python3 không kèm script), runner sẽ báo lỗi chứ không giả vờ coi một lần thoát 0 giây là một job thật.
Khi lệnh kết thúc, bạn nhận được phần quan trọng nhất: exit code và thời gian đã chạy, trình bày rõ ràng (12m 34s, 1h 23m). Bạn luôn nhận được thông báo local; nếu đã cấu hình một push provider — ntfy.sh (miễn phí) hoặc Pushover — thông báo cũng tới điện thoại, và trên Pro bạn còn có thể phân phối thêm sang Telegram, Discord, Slack, hoặc một webhook URL tùy ý. Nếu bạn bắt đầu một phiên chạy trên pin, LidRun sẽ báo trước ngưỡng tự dừng bạn đã cấu hình (mặc định 20%) ngay từ đầu, chứ không để bạn phát hiện ra sau.
Đây cũng là khác biệt an toàn thành thật so với một assertion caffeinate trơn: nếu pin tụt qua ngưỡng đó, hoặc Mac chạm mức áp lực nhiệt nghiêm trọng, LidRun không lặng lẽ buông assertion ra rồi cầu may — nó gửi một tín hiệu terminate thật sự tới lệnh đang chạy, y như khi bạn bấm Stop. Đó là một sự gián đoạn thật, không phải một gợi ý nhẹ nhàng, nên nó bảo vệ tốt hơn một wake lock mù quáng — dù vậy, một job bị dừng vài phút trước khi tự hoàn thành vẫn là một sự gián đoạn, không phải một đảm bảo rằng lần chạy đó sẽ xong. Và giống caffeinate, cái này chỉ giữ Mac thức qua idle sleep, không giữ được qua việc đóng nắp máy vật lý — kết hợp với Closed-Lid mode (hoặc màn hình ngoài) nếu bạn định đóng nắp lại.
Điều tương tự từ dòng lệnh
Nếu bạn sống trong terminal, lidrun -- <command> [args...] làm đúng việc gắn vòng đời tương tự, và khác với Run & Watch, nó miễn phí và hoàn toàn tự thân — vẫn hoạt động kể cả khi app LidRun không chạy, vì nó tự giữ một assertion IOKit ngắn hạn riêng cho tiến trình được bọc, rồi giải phóng khi tiến trình đó kết thúc. lidrun -- ./run-migrations.sh hay lidrun -- npm test đọc lên y hệt như gõ thẳng lệnh, vì bên dưới nó đúng là vậy — cùng login shell, cùng cách resolve PATH. Ctrl-C hủy nó như bất kỳ job foreground nào.


Vì nó tự thân như vậy, nên phải nói thẳng cho rõ: lidrun -- không tự kiểm tra phần trăm pin hay trạng thái nhiệt — cơ chế giám sát đó nằm trong tính năng GUI Run & Watch, gắn với các cài đặt an toàn thông thường của app. Nếu chạy trên pin cho một job kéo dài hàng giờ, đây là điều nên biết trước khi bạn đẩy nó chạy nền rồi bỏ đi; dù đi đường nào thì cắm sạc vẫn là lựa chọn an toàn hơn.
Có một cái bẫy thực sự đáng biết nếu bạn muốn nối nhiều hơn một lệnh trong cùng một phiên keep-awake: hãy quote toàn bộ thành một tham số shell duy nhất. lidrun -- 'npm test && npm run build' chạy đúng — cả hai lệnh chạy trong cùng một phiên được bọc. Bọc thêm một shell nữa thay vào đó, kiểu lidrun -- bash -c 'npm test && npm run build', thì lại không đúng: LidRun nối lại danh sách tham số bằng dấu cách trơn trước khi chạy lại qua login shell, việc này âm thầm làm mất phần quote bên trong và có thể bỏ sót một phần lệnh. Quote thành một tham số duy nhất mới là cách chắc chắn. Cả hai cách đều trả về đúng exit code của lệnh gốc làm exit code của chính nó, nên có thể đưa thẳng vào chuỗi &&/|| của một script hay kiểm tra $? y hệt như chạy lệnh trực tiếp.
Chạy bằng CLI hay GUI — chọn cái nào
Chọn CLI khi bạn vốn đã định gõ lệnh đó rồi, muốn đưa vào script hay một workflow chạy qua terminal, hoặc bạn không cần (hay không có) Pro. Exit code trả về ngay cho shell của bạn, và chẳng có gì cần mở hay cấu hình cả.
Chọn GUI Run & Watch a Command khi bạn muốn cơ chế tự dừng theo pin/nhiệt thực sự theo dõi job — chứ không chỉ giữ assertion rồi cầu may — hoặc khi bạn muốn nhận thông báo trên điện thoại mà không cần mở sẵn cửa sổ terminal, hoặc khi xem đuôi output trực tiếp trong menu bar thoải mái hơn một tab terminal. Nó cần license Pro; đường CLI là phiên bản miễn phí tương đương cho mọi thứ, trừ cơ chế giám sát an toàn đó và việc phân phối thông báo đa kênh.
Dù chọn đường nào, đích đến vẫn là một: công việc hoàn tất, kết quả rõ ràng, và Mac được tự do ngủ lại thay vì bị bỏ thức bởi một cái công tắc chẳng ai tắt lại.
Nó phù hợp ở đâu
Hãy nghĩ tới những job bạn bấm chạy rồi đi làm việc khác: một migration database dài mà bạn muốn xác nhận đã chạy sạch sẽ, một bộ test suite đầy đủ mất hai mươi phút mà bạn không muốn ngồi canh, một export dữ liệu bật trước khi ngủ và cần xong trước sáng. Với tất cả những trường hợp đó, bản báo cáo ở cuối chiếm nửa giá trị — biết được exit code và thời gian chạy cho bạn biết migration đã áp dụng chưa, suite có pass không, export có thực sự xong không, mà không cần cuộn xem lại output.
Khi đi đường GUI, nó chạy trong cùng ranh giới an toàn như mọi thứ khác trong app: vượt qua ngưỡng pin bạn đã cấu hình, hoặc chạm mức áp lực nhiệt nghiêm trọng, LidRun sẽ dừng lệnh lại hẳn thay vì ép phần cứng gánh tiếp. Đường CLI không có sẵn cơ chế giám sát đó, nên với một lần chạy qua đêm không ai để mắt tới, cắm sạc và đặt máy trên một mặt phẳng cứng, thoáng khí vẫn là lựa chọn đúng bất kể bạn đi đường nào — điều này giúp giảm rủi ro cho một lần chạy dài không ai giám sát, chứ không hứa hẹn job sẽ sống sót bất kể phần cứng đang ở trạng thái nào.
LidRun giữ công việc của bạn chạy tiếp khi đóng nắp, với cơ chế bảo vệ pin và nhiệt tích hợp sẵn.
Đã có LidRun? Đọc hướng dẫn thiết lập →
Mới biết đến LidRun? Xem bảng giá →
Câu hỏi thường gặp
Có. Run & Watch a Command giữ keep-awake suốt vòng đời của lệnh và giải phóng ngay khi lệnh kết thúc, nên Mac chỉ thức đúng bằng thời gian job chạy — cùng hành vi mà caffeinate cho bạn miễn phí khi truyền lệnh vào nó, cộng thêm phần báo cáo và cơ chế giám sát an toàn mà caffeinate không có.
Nó báo exit code và thời gian đã chạy (trình bày kiểu 12m 34s hoặc 1h 23m), và luôn gửi một thông báo local. Nếu bạn đã cấu hình push provider (ntfy.sh hoặc Pushover), thông báo cũng tới điện thoại, và trên Pro bạn còn có thể phân phối thêm sang Telegram, Discord, Slack, hoặc một webhook tùy ý.
Có — chạy lidrun -- your-command và LidRun sẽ giữ một assertion keep-awake ngắn hạn riêng cho lệnh đó, giải phóng khi lệnh kết thúc. Nó miễn phí và hoàn toàn tự thân, hoạt động được kể cả khi app không chạy. Tính năng GUI Run & Watch a Command (đuôi output trong menu bar, thông báo, tự dừng theo pin/nhiệt) là tính năng Pro; CLI wrapper là phiên bản miễn phí tương đương, chỉ thiếu cơ chế giám sát an toàn đó.
Không, tự nó thì không. Cả đường GUI lẫn CLI chỉ ngăn idle sleep, không ngăn được forced sleep mà macOS kích hoạt khi bạn đóng nắp máy vật lý mà không có màn hình ngoài. Với một job đóng nắp, hãy kết hợp thêm Closed-Lid mode (hoặc dùng màn hình ngoài, setup clamshell kinh điển) cùng với Run & Watch.
Trên đường GUI, khi vượt qua ngưỡng pin bạn đã cấu hình (mặc định 20%) hoặc chạm mức áp lực nhiệt nghiêm trọng, LidRun sẽ thực sự dừng lệnh đang chạy, chứ không chỉ buông assertion keep-awake rồi để nó chạy nền không được bảo vệ. Wrapper lidrun -- của CLI không có sẵn kiểm tra này — nó chỉ giữ một assertion trơn suốt vòng đời tiến trình bất kể mức pin — nên với một job chạy qua đêm không ai để mắt tới, cắm sạc vẫn là lựa chọn an toàn hơn cho dù bạn đi đường nào.
Không có timeout sẵn ở cả hai đường. Một lệnh bị treo sẽ giữ assertion cho tới khi nó thoát, bạn tự dừng nó (nút Stop trên GUI, Ctrl-C trên CLI), hoặc — chỉ trên đường GUI — cơ chế giám sát an toàn theo pin/nhiệt sẽ dừng nó thay bạn.
Có, nếu bạn quote toàn bộ thành một tham số duy nhất: lidrun -- 'npm test && npm run build' chạy đúng cả hai lệnh trong cùng một phiên được bọc. Bọc thêm một shell nữa thay vào đó, kiểu lidrun -- bash -c '...', có thể âm thầm bỏ sót một phần lệnh do cách danh sách tham số bị nối lại — hãy quote lệnh ghép thành một tham số, chứ không phải như tham số cho một shell lồng bên trong.
View trực tiếp trên GUI hiển thị phần đuôi cuộn liên tục của 200 dòng output gần nhất — đủ để xác nhận job còn sống và xem tiến độ gần đây, nhưng đây là view trực tiếp, không phải file log được lưu lại. Hãy redirect output của lệnh ra file nếu bạn cần bản ghi đầy đủ để xem sau.