終端機手動執行成功,排程卻沒有產出檔案,先別不停重加同一條cron。互動式shell裡已有的PATH、變數與目前目錄,排程執行時未必相同;命令可能根本找不到,或已開始執行卻讀錯相對路徑。
先區分「排程沒有啟動」和「命令啟動後失敗」。本文處理後者常見的環境差異,用受限環境逐項重現;排程時間、daemon與實際觸發紀錄仍要另外核對,不能拿一次手動成功替代。
先記下失敗時用哪個使用者、哪條命令
保存完整命令、執行身分與錯誤輸出。使用者自己的crontab、系統crontab與其他排程器,不一定使用同一個帳號或相同格式。不要把自己登入後的成功結果,直接當成網站服務帳號也能讀寫。
在有權限的環境查看該帳號的排程與既有執行紀錄,確認命令真的開始過。沒有產出檔案,不等於沒有啟動;命令找不到、設定讀不到、輸出目錄不能寫,都可能在很早的步驟退出。
把互動環境收窄,找到第一個差異
env -i讓命令從空的環境開始,再明確提供需要的變數。這不是完整複製所有cron實作,而是用來排除「因為終端機碰巧已有設定才成功」的條件。
env -i PATH=/usr/bin:/bin TASK_MODE=demo \
/bin/sh /srv/site/job.sh
路徑與TASK_MODE是例子,要換成自己的腳本與非敏感設定。不要把正式密碼、Token或完整環境變數清單貼到公開日誌;只記排查所需的安全欄位,例如工作目錄、命令位置與結束碼。
本文的專用容器有一個報表helper,放在自己的tools目錄;它需要TASK_MODE=demo,並讀取目前目錄的config.txt。先在site目錄提供完整PATH與變數,結果成功,再只改一個條件。
條件 結果
完整PATH+site目錄+demo 成功,結束碼0
PATH不含tools 找不到helper,結束碼127
工作目錄改成other 找不到config.txt,結束碼1
沒有TASK_MODE 模式檢查失敗,結束碼2
明確路徑與目錄的wrapper 成功,結束碼0
這些是隔離腳本的實際結果,碼2來自本文helper自己的檢查,不是所有程式都用2表示環境變數缺失。案例沒有安裝cron,也沒有等待定時觸發;它驗證的是受限環境中命令如何失敗與恢復。
PATH影響找程式,不決定讀檔目錄
PATH用來尋找未寫完整路徑的可執行命令。你在終端機能呼叫node、php或自建helper,可能因登入設定已把它加入PATH。排程不一定讀取相同的啟動檔,也不會自動取得你的alias或shell函式。
先用command -v核對實際程式位置,再確認執行版本。需要固定某個runtime時,使用已核對的絕對路徑,或在自己的wrapper明確安排PATH;不要把互動式shell整份設定無差別source進排程。
command -v php
command -v node
pwd
找到程式不表示相對設定檔也找得到。config.txt、./output與相對日誌路徑,通常由目前工作目錄解析;腳本放在/srv/site,不代表執行時就自動在/srv/site。
把工作目錄與必要設定寫清楚
可以讓專用wrapper先切到確定的目錄,失敗就停止,再以絕對路徑呼叫工具。下列片段示範這個責任分配,不包含排程安裝:
#!/bin/sh
cd /srv/site || exit 1
TASK_MODE=demo /srv/site/tools/wn-report-helper
不要省略cd失敗的處理,否則目錄不存在時,腳本可能繼續在另一個位置工作。設定檔也可以改用明確絕對路徑,但要讓維護者知道由哪個位置讀取,避免一半相對、一半絕對而難以追查。
需要的變數由應用自己的設定方式提供。互動shell裡只是定義而沒有export的變數,不一定傳給子程序;即使傳給了手動執行,也不會因此永久寫進排程環境。先核實來源,不要為了成功而把所有私人設定複製過去。
相對路徑的影響不只在讀取。若腳本寫入./output,換一個工作目錄可能把產物寫到別處,畫面上就像沒有完成。先記下實際輸出位置,確認它是在預期目錄;不要為了找到檔案就用全機搜尋暴露其他資料。
檔案換成絕對路徑後,也要確認它確實是目前使用的版本。部署把current連結換到新目錄,排程卻仍指向舊release的腳本,可能一直跑舊設定;保存命令與實際解析路徑,才知道測的是否同一份程式。
shell不同,也可能改變結果
cron通常使用設定的SHELL執行命令;此處引用的Cronie手冊預設為/bin/sh。若腳本用了Bash專用語法,就要明確安排適合的解譯器,不要在終端機用Bash測過,排程卻用sh呼叫同一段內容。
直接執行腳本還受執行權限、shebang與掛載選項影響。本文第一次測試的tmpfs預設禁止執行,完整PATH也得到Permission denied;在自己的隔離掛載允許執行後,才分離出PATH與目錄差異。正式環境看到相同錯誤,應先核對限制用途,不要直接放寬整個掛載點。
排程命令還有自己的解析規則。例如Cronie會處理未跳脫的百分比符號,終端機裡可用的date格式字串,原樣貼到crontab不一定相同。複雜內容放進有版本紀錄的腳本,通常比把一大串命令擠在排程欄位裡更容易維護。
讓輸出能回答「失敗在哪一步」
把標準輸出與錯誤輸出寫到已存在、且執行帳號可寫的專用日誌位置。輸出重新導向由shell先處理;日誌目錄不存在或不能寫,命令甚至可能還沒開始就退出。
記錄開始時間、安全的工作目錄、結束碼與短錯誤即可,不要每次把env全列出來。保留原排程與腳本版本,先在自有測試範圍調整一項,再用同一個帳號和受限環境比較。
命令條件修好後,再驗真正的排程
受限環境成功,只證明這個命令具備所需條件。接著在真正到期時間核對排程器紀錄、腳本開始與完成,以及預期產物。daemon未運作、時間或時區不符、排程未載入,仍是另外的問題。
不要用反覆手動執行掩蓋沒有定時觸發的狀況。也別為了測試直接增加正式備份或資料寫入的頻率;先確認任務是否可重複、是否會重疊與產生副作用。環境、執行與產物分開核實,維護者才能知道真正修好了哪一段。

評論0