2008-10-10 69 views
47

我正在尋找方式來檢測PHP腳本是否從shell上的手動調用(我登錄並運行它)運行,或者它是否從crontab條目運行。PHP可以檢測它是否從cron作業或命令行運行?

我有用php編寫的各種維護類型腳本,我已經設置在我的crontab中運行。有時候,我需要提前手動運行它們,或者如果出現故障/中斷,我需要運行它們幾次。

問題在於,我還有一些外部通知設置到任務中(張貼到Twitter,發送電子郵件等),我不希望每次手動運行腳本時都會發生這種情況。

我使用php5(如果它很重要),它是一個相當標準的linux服務器環境。

任何想法?

+2

作爲鏈接問題添加:[檢測PHP腳本是否正在交互式運行](http://stackoverflow.com/questions/11327367/detect-if-a-php-script-is-being-run -interactively有或沒有/ 11327451) – Leigh 2012-07-04 11:30:20

+0

@Leigh:感謝您的參考! – hakre 2015-11-06 11:04:32

回答

39

而不是檢測腳本從crontab運行的時間,它可能更容易檢測手動運行時。

當您從命令行運行腳本時,會設置很多環境變量(在$ _ENV數組中)。這些是什麼,將取決於您的服務器設置和手動運行腳本時從cron運行時,不存在你的登錄方式在我的環境,下面的環境變量設置:

  • TERM
  • SSH_CLIENT
  • SSH_TTY
  • SSH_CONNECTION

有別人了。因此,舉例來說,如果你總是使用SSH訪問的方塊,然後如果腳本從cron運行下面一行將檢測:

$cron = !isset($_ENV['SSH_CLIENT']);

+1

查看後,這似乎是最好的方法。在_ENV + _SERVER之間,我很確定我可以自信地檢測到誰/在哪裏/誰在運行它,並據此採取行動。 CRON = crontab中的運行線也會有幫助。 – Uberfuzzy 2008-10-10 14:15:32

+1

似乎默認的php.ini已經隨着時間的推移而改變,不會默認填充$ _ENV全局。一個安全的方法是使用getenv('SSH_CLIENT') - 在這裏看到更多的信息http://stackoverflow.com/questions/3780866/why-is-my-env-empty – GuruBob 2015-09-17 20:53:46

5

不是我所知道的 - 可能最簡單的解決方案是自己提供一個額外的參數來告訴腳本如何調用它。

4

我想看看$_ENV(var_dump()它),並檢查當你運行它時與cronjob運行時是否有差異。除此之外,我不認爲有一個「官方」開關告訴你發生了什麼。

2

在cron命令中,將?source=cron添加到腳本路徑的末尾。然後,在您的腳本中,檢查$_GET['source']

編輯:對不起,這是一個shell腳本,所以不能使用qs。我認爲,你可以以php script.php arg1 arg2的形式傳遞參數,然後用來讀取它們。

+2

這是一個shell腳本,而不是一個網頁。 – 2008-10-10 10:50:20

27

你可以設置一個額外的參數,或添加一行在crontab,也許是:

CRON=running 

然後你就可以檢查你的環境變量「CRON」。另外,嘗試檢查$ SHELL變量,我不確定/ cron設置它。

+0

我從來不知道你可以把其他命令/變量行放在crontab中。我只學過定時命令的語法。這加上PHP的$ _ENV和$ _SERVER,將有助於我想要實現的。謝謝 – Uberfuzzy 2008-10-10 15:44:21

+0

「男人5 crontab」爲所有堅韌的細節。但是,您可以設置環境變量。通常這是用來設置PATH或SHELL,但你可以設置你喜歡的任何東西。 – 2008-10-10 21:48:28

+1

只是想指出,`variables_order`必須包括「E」在PHP。ini以便爲$ _ENV數組填充 – andrewtweber 2012-04-12 21:19:23

6

我不明白PHP的具體情況,但是你可以走上進程樹,直到找到init或cron。

假設PHP可以獲得它自己的進程ID和運行外部命令,就應該執行ps -ef | grep pid的無論身在何處PID是你自己的進程ID,並從中提取父進程ID(PPID)。

然後對該PPID執行相同的操作,直到您作爲父代或作爲父代的init達到cron。

例如,這是我的進程樹,你可以看到所有權鏈,1 - > 6386 - > 6390 - > 6408

UID  PID PPID C STIME TTY  TIME CMD 
root  1  0 0 16:21 ?  00:00:00 /sbin/init 
allan 6386  1 0 19:04 ?  00:00:00 gnome-terminal --geom... 
allan 6390 6386 0 19:04 pts/0 00:00:00 bash 
allan 6408 6390 0 19:04 pts/0 00:00:00 ps -ef 

下cron運行將類似於相同的處理:

UID  PID PPID C STIME TTY  TIME CMD 
root  1  0 0 16:21 ?  00:00:00 /sbin/init 
root 5704  1 0 16:22 ?  00:00:00 /usr/sbin/cron 
allan 6390 5704 0 19:04 pts/0 00:00:00 bash 
allan 6408 6390 0 19:04 pts/0 00:00:00 ps -ef 

「走進過程樹」解決方案意味着您不必擔心引入仿真參數來指示您是否在cron下運行 - 您可能會忘記在交互式會話中執行此操作,填滿東西。

+0

大概你可以用'無參數'這種方式來設置它,這意味着你正在交互式運行,這樣你就不會忘記它。雖然有趣的解決方案。 – 2008-10-10 11:24:55

+0

是的,非常有趣的做法。也可以使用其他非PHP的東西。 – Uberfuzzy 2008-10-10 13:48:43

1

$_SERVER['SESSIONNAME']包含Console如果從CLI運行。也許這有幫助。

0
if(!$_SERVER['HTTP_HOST']) { 
blabla(); 
} 
0

我認爲這將是更好地與你不會手動運行命令行的附加選項運行的cron條命令。

的cron會做:

command ext_updates=1 

手冊會做:

command 

只需添加腳本本身的選項有ext_updates參數去有假的默認值。

3

在我的環境中,我發現TERM$_SERVER中設置,如果從命令行運行,但如果通過Apache作爲web請求運行,則不會設置。我把這個在我的腳本的頂部,我可能會在命令行中運行,或通過網絡瀏覽器可以訪問:

if (isset($_SERVER{'TERM'})) 
{ 
    class::doStuffShell(); 
} 
else 
{ 
    class::doStuffWeb(); 
} 
14

正確的方法是使用上的例如posix_isatty()函數標準輸出文件描述符,像這樣:

if (posix_isatty(STDOUT)) 
    /* do interactive terminal stuff here */ 
0

posix_isatty(STDOUT) return FALSE如果CLI調用的輸出重定向(管道或文件)...

-4

很容易的,我......只是count($_SERVER['argc']),如果你有結果高於零時,它將耗盡服務器。您只需添加到您的自定義變量$_SERVER['argv'],如"CronJob"=true;

3

令人毛骨悚然。嘗試

if (!isset($_SERVER['HTTP_USER_AGENT'])) { 

改爲。 PHP客戶端二進制文件不發送它。術語類型只在PHP用作模塊(即apache)時有效,但通過CGI接口運行php時,請使用上面的示例!

24

這是我用來發現其中腳本是從執行。看看php_sapi_name功能的更多信息:http://www.php.net/manual/en/function.php-sapi-name.php

$sapi_type = php_sapi_name(); 
if(substr($sapi_type, 0, 3) == 'cli' || empty($_SERVER['REMOTE_ADDR'])) { 
    echo "shell"; 
} else { 
    echo "webserver"; 
} 

編輯: 如果php_sapi_name()不包括CLI(可能是CLIcli_server)然後我們檢查,如果$_SERVER['REMOTE_ADDR']是空的。從命令行調用時,它應該是空的。

14

我認爲最普遍的解決方案是一個環境變量添加到cron命令,並尋找它的代碼。它將在每個系統上工作。

如果由cron執行的命令,例如:

"/usr/bin/php -q /var/www/vhosts/myuser/index.php" 

將其更改爲

"CRON_MODE=1 /usr/bin/php -q /var/www/vhosts/myuser/index.php" 

然後你可以檢查它的代碼:

if (!getenv('CRON_MODE')) 
    print "Sorry, only CRON can access this script"; 
24
if (php_sapi_name() == 'cli') { 
    if (isset($_SERVER['TERM'])) { 
     echo "The script was run from a manual invocation on a shell"; 
    } else { 
     echo "The script was run from the crontab entry"; 
    } 
} else { 
    echo "The script was run from a webserver, or something else"; 
} 
0

這很簡單。 Cron守護程序始終導出MAILTO環境變量。檢查它是否存在並具有非空值 - 然後從cron運行。

2
getenv('TERM') 

填充SO的30個字符分鐘。

1

另一種選擇是測試當PHP文件是通過Web調用,如果通過命令行拼命地跑不設置設置一個特定的環境變量。

。如果APACHE_RUN_DIR環境變量設置這樣我測試我的Web服務器:

if (isset($_ENV["APACHE_RUN_DIR"])) { 
    // I'm called by a web user 
} 
else { 
    // I'm called by crontab 
} 

,以確保它會在您的Web服務器上運行,你可以把一個虛擬的PHP文件的網站服務器與此單個語句:

<?php var_dump($_ENV); ?> 

然後1)使用Web瀏覽器加載以及2)命令行加載像這樣

/usr/bin/php /var/www/yourpath/dummy.php 

比較的差異,並測試適當的變量。