依賴反轉原則

Depend on abstractions, not on concretions.
Robert C. Martin

依賴反轉原則,全名 Dependency Inversion Principle,簡稱 DIP

定義

高階模組不應該依賴低階模組,兩者應該要依賴其抽象,抽象不要依賴細節,細節要依賴抽象。
不要把程式碼寫死某種實作上。

秘訣

  • 互動的部分交給抽象類別(abstract class)或介面(interface)
  • 會改變的實作,就放到子類別裡面

提醒

設計階段就可以先以抽象層次的互動來避開這個問題
抽象層次不要太高,以免無法對焦於真正關注的類別特徵

範例

來看個例子。
有一位計程車司機,他開著 Toyota 的車來工作。
但如果有天他換車了,換開 Nissan,原本的 drive function 就不能用了…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class TaxiDriver
{
public $name;
public function __construct($name)
{
$this->name = $name;
}

public function drive(Toyota $toyota)
{
...
}
}

$driver = new TaxiDriver();
$driver->drive(new Toyota);

那來改一改吧!
不要寫死成開 Toyota,改成開車就好了(依賴抽象),這樣隨時換開什麼車都可以。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
abstract car
{
private $name;

public function __construct($name)
{
$this->name = $name;
}
}

class Toyota extends Car
{
...
}

class Nissan extends Car
{
...
}

class TexiDriver
{
public $name;
public function __construct($name)
{
$this->name = $name;
}

public function drive(Car $car)
{
...
}
}

$taxiDriver = new TexiDriver();
$taxiDriver->drive(new Toyota);
$taxiDriver->drive(new Nissan);

然後我們可以發現,如果需要的話,計程車司機(TexiDriver)還可以繼承人(Human)類別,然後 drive function 抽出去成介面(interface),TexiDriver 再實作 driver 介面,都可以再向上抽象,增加使用的彈性。

Reference

PHP 也有 Day #19 - PHP 返樸歸真系列之從實例學設計模式 by 大澤木小鐵 (Jace Ju)​
物件導向設計原則 SOLID
SOLID:五則皆變
The Principles of OOD

介面隔離原則

Make fine grained interfaces that are client specific.
Robert C. Martin

介面隔離原則,全名 Interface segregation principle,簡稱 ISP

定義

用戶端程式碼不應該依賴它用不到的介面,依賴的介面都是有其必要性。
把不同功能的從介面中分離出來。

其實我只是要打個果汁
圖片來源

秘訣

  • 把 Interface 當成「可以做什麼」, 而不是「是一個什麼」
  • 減少讓每個 Interface 可以做的事
  • 如果發現有空實作時,就表示 Interface 可以再細化

範例

來看看範例。
身為一個沒朋友的程序猿,最好的朋友就是家裡的毛小孩。
那就先來寫個寵物類別,再來條狗和一隻貓,最好還要能跟我玩飛盤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
interface Pet
{
public function eat(): void;
public function sleep(): void;
public function catchFrisbee(): void;
}

class Dog implements Pet
{
public function eat(): void
{
...
}

public function sleep(): void
{
...
}

public function catchFrisbee(): void
{
...
}
}

class Cat implements Pet
{
public function eat(): void
{
...
}

public function sleep(): void
{
...
}

public function catchFrisbee(): void
{
//cats do not catch frisbees
throw new Exception('大膽刁民,竟敢造次!(抓)');
}
}

醒醒吧!貓皇是不會和你玩飛盤的…
但是因為是實作了 Pet 介面,硬著頭皮還是要實作 catchFrisbee,
結果你家主子不理你,還違反里氏替換原則了…
所以,還是不要這樣搞了吧!再來改一改…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
interface Pet
{
public function eat(): void;
public function sleep(): void;
}

interface Play
{
public function catchFrisbee(): void;
}

class Dog implements Pet
{
public function eat(): void
{
...
}

public function sleep(): void
{
...
}
}

class TrainedDog implements Pet, Play
{
public function eat(): void
{
...
}

public function sleep(): void
{
...
}

public function catchFrisbee(): void
{
...
}
}

class Cat implements Pet
{
public function eat(): void
{
...
}

public function sleep(): void
{
...
}
}

這次我們把接飛盤這個動作從 Pet 類別抽出來,單獨定義成一個 Play 介面,以後不要再強迫你的主子陪你玩飛盤了。然後,再定義一個 TrainedDog 類別,只有這個類別實作 Play 介面,畢竟狗狗要能接飛盤也是要經過訓練的嘛!

Reference

PHP 也有 Day #19 - PHP 返樸歸真系列之從實例學設計模式 by 大澤木小鐵 (Jace Ju)​
物件導向設計原則 SOLID
SOLID:五則皆變
The Principles of OOD

最小知識原則

最小知識原則,全名 Least Knowledge Principle,簡稱 LKP

定義

一個物件應該對其他物件有最少的了解,盡可能減少類別中的 public method,降低其他類別對此類別的偶合度。

你知道的太多了

秘訣

  • 已經成形的操作流程就封裝起來
  • 不必要的公開方法就設為 private 或 protected
  • 外面知道的越少,偶合度就越低

範例

假設我們今天要開一間洗衣店,首先寫個洗衣服務。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class LaundryService
{

private $clothing;

public function __constrcut(array $clothing)
{
$this->clothing;
}

public function cleanSpecialStain()
{
...
}

public function wash()
{
...
}

public function dry()
{
...
}

public function iron()
{
...
}
}

$service = new LaundryService($dirtyClothes);
$service->cleanSpecialStain()
->wash()
->dry()
->iron();

嗯,看起來很不錯。
從清除特殊污漬、清洗、烘乾、到整燙,服務很完整。
SOP 流程很明暸, 但是有一個問題,想使用這個洗衣服務的人都居然要知道這個固定的 SOP!!!
既然這是固定的作業流程,我們還是將它封裝起來吧!免得有人不照著流程亂搞。或是有一天老闆佛心來地想要把除毛球服務加到固定流程裡,但是客人不知道。

所以,再來把這個 SOP 流程給包裝起來,讓使用這個服務的人只要呼叫 doLaundry 函式就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

class LaundryService
{

private $clothes;

public function __constrcut(array $clothes)
{
$this->clothes;
}

public function doLaundry()
{
$this->cleanSpecialStain()
->wash()
->dry()
->iron();
}

private function cleanSpecialStain()
{
...
}

private function wash()
{
...
}

private function dry()
{
...
}

privat function iron()
{
...
}
}

$service = new LaundryService($dirtyClothes);
$service->doLaundry();

Reference

PHP 也有 Day #19 - PHP 返樸歸真系列之從實例學設計模式 by 大澤木小鐵 (Jace Ju)​
物件導向設計原則 SOLID
SOLID:五則皆變
The Principles of OOD

里氏替換原則

Derived classes must be substitutable for their base classes.
Robert C. Martin

里氏替換原則,全名 Liskov substitution principle,簡稱 LSP

定義

子類別要能替換掉父類別而不影響程式架構,並且所有父類別能做的事,子類別也要可以做

秘訣

  • 對介面寫程式,重點放在要執行的動作
  • 方法簽名、回傳值與丟出的異常要一致
  • 思考繼承時子類別所造成的「行為變化」要如何設計才不會違反父類別介面的合約

提醒

  • 依賴子類別的地方不能用父類別取代
  • 拋棄繼承,思考可否改用組合的方式

範例

來看看範例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Scooter
{
private $name;
private $fuelTankCapacity;
public function getName(){};
public function getFuelTankCapacity(){};
}

class Yamaha extends Scooter()
{
...
}

class Kymco extends Scooter()
{
...
}

class Gogoro extends Scooter()
{
...
}

是不是有點怪怪的…Gogoro 沒有油箱啊!!!
子類別並沒有完全實作父類別有的功能,違反了里氏替換原則。
那麼來稍微改一下程式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ElectricScooter
{
private $name;
private $batteryCapacity;
public function getName(){};
public function getBatteryCapacity(){};
}

class Gogoro extends ElectricScooter()
{
...
}

class Scooterist
{
private $scooter;
public function setScooter(Scooter $scooter)
{
$this->scooter = $scooter;
}
}

$yamaha = new Yamaha();
$scooterist = new Scooterist();
$Scooterist->setScooter($yamaha);

另外定義一個 ElectricScooter 類別,然後讓 Gogoro 去繼承它,就解決了這個問題。
並且每個子類別都可以替代其父類別,也符合里氏替換原則。最後,也許再把動力來源抽出來定義成介面會更好,不過還是要實際情況來考慮。

Reference

PHP 也有 Day #19 - PHP 返樸歸真系列之從實例學設計模式 by 大澤木小鐵 (Jace Ju)​
物件導向設計原則 SOLID
SOLID:五則皆變
The Principles of OOD

封閉開放原則

You should be able to extend a classes behavior, without modifying it.
Robert C. Martin

封閉開放原則,全名 Open-closed Principle,簡稱 OCP。

定義

對擴充開放,對修改封閉。

第一次看到的話是不是很匪夷所思,要怎麼新增功能又不修改程式碼呢?
其實是新增功能(擴充),但是不修改舊有的功能(對修改封閉),也就是只要把程式寫成在之後要新增功能時,不需要改動原本有的功能就好了。

怎麼做呢?

實踐這個原則的手法有不少,還是要依照實際的情境選擇適合的方法。
以下是可以參考的作法:

  • 利用繼承
  • 利用抽象介面
  • 依賴倒轉 (Dependency Injection pattern, DIP)
  • 裝飾者模式
  • 策略模式

秘訣

如果還是不知道麼開始的話,試著用抽象層級的方式去思考類別,不要把功能寫死,試著寫成抽象的概念,然後把會造成改變的選項較給別的類別處理,主要類別還是專心處裡核心任務。最後,通常變化都是後來才出現的,一開始設計的時候,不一定會構思到要這樣寫,之後再透過重構完成即可。

範例

看個範例吧!
下面這一段程式碼,假設我們有一台 Nintendo 主機,然後我們手邊有三款遊戲可以玩,要玩的時候呼叫 play function 就可以了。
但是,如果我每買一款遊戲就要新增遊戲在這個 Nintendo 類別裡面…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Nintendo
{
public function play(string $game)
{
switch ($game) {
case 'mario':
$mario = new Mario();
$mario->start();
case 'car-racing':
$carRacing = new CarRacing();
$carRacing->start();
case 'pokemon':
$pokemon = new Pokemon();
$pokemon->start();
}
}
}

試著改改看…
把 game 抽出來寫成介面,定義一些所有遊戲都會需要的基本功能,之後每一款遊戲再各自去實作細節。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
interface Game 
{
public function start();
}

class Mario implements Game
{
protected $mario;
protected $wario;
protected $luigi;
...
public function __construct()
{
//initialize characters...
}
public function start()
{
...
}
}

class CarRacing implements Game
{
public function start()
{
...
}
}

class Pokemon implements Game
{
public function start()
{
...
}
}

接下來,把 Nintendo 改成這樣

1
2
3
4
5
6
7
8

class Nintendo
{
public function play(Game $game)
{
$game->start();
}
}

以後,每新增一款遊戲(對擴充開放),我們也不會動到 Nintendo 了(對修改封閉),喔耶!
而且 Nintendo 在呼叫 play 時,也不用管你是什麼遊戲,只要讓遊戲呼叫 start 就好了。

Reference

PHP 也有 Day #19 - PHP 返樸歸真系列之從實例學設計模式 by 大澤木小鐵 (Jace Ju)​
物件導向設計原則 SOLID
SOLID:五則皆變
The Principles of OOD

單ㄧ職責原則

A class should have one, and only one, reason to change.
Robert C. Martin

單ㄧ職責原則,全名 Single Responsibility Principle,簡稱 SRP
個人覺得 SRP 是 SOLID 六大原則中最簡白易懂的原則。

定義

應該且僅有一個原因引起類別的變更,讓類別只有一個職責。

秘訣

  • 關注點分離,一個類別只應有一個核心任務
  • 不要貪方便全塞一起
  • 注意切太細會有類別太多的問題

提醒

  • 設計階段就可以避開類別職責太大的問題
  • 但小心在維護階段受到誘惑又讓類別職責變多,所以,想清楚類別的核心價值,若是需要開新的類別,當開則開

範例

先來看一個例子,下面這個例子是否有可以優化的地方?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Order
{
private $products;
private $amount;

public function calculateAmount()
{
if ($this->products) {
// calculate total amount...
}
}

public function printInvoice()
{
//download a PDF invoice
}

public function printPackingList()
{
//download a PDF packing list
}
}

稍微修改一下,把輸出檔案的部分拆出來,這樣 Order 是不是看起來就更能專注在核心的工作項目上,有關輸出成單據的部分都拆出去,之後也可能會有不同的單據要輸出,擴充性也能更好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Order
{
private $products;
private $amount;

public function calculateAmount()
{
if ($this->products) {
// calculate total amount...
}
}
}

interface Printable
{
public function print(Order $order);
}

class InvoicePrinter implements Printable
{
public function print(Order $order)
{
$contensts = $order->getContents();
//prepare PDF then download...
}
}

class PackingListPrinter implements Printable
{
public function print(Order $order)
{
$contensts = $order->getContents();
//prepare PDF then download...
}
}

Reference

PHP 也有 Day #19 - PHP 返樸歸真系列之從實例學設計模式 by 大澤木小鐵 (Jace Ju)​
物件導向設計原則 SOLID
SOLID:五則皆變
The Principles of OOD

淺談 SOLID

前言

你該學的不是設計模式
而是物件導向開發原則
大澤木小鐵

初學者在學習設計模式的時候,常常一知半解,學了以後好像也不是能很順利並適當地使用在專案上,筆者也是如此。直到看過鐵哥講解 SOLID 後,才意識到在使用設計模式之前,應該先好好學習 SOLID 的精神。鐵哥曾說過如果能掌握 SOLID,有沒有一定要使用設計模式也就沒有那麼重要了。可以想見,SOLID 是多麽重要的一個概念。

SOLID

SOLID 是由 Robert C. Martin(Uncle Bob)在 Agile software development (2002) 一書中提到的五個設計原則。很推薦做為物件導向程式開發時的一些心法,是五個概念的縮寫,但是在學習 SOLID 時有的文章也會列入 LKP ,所以筆者也將之列了進來:

單一職責原則 (Single responsibility principle, SRP)
開放封閉原則 (Open-Close principle, OCP)
里氏替換原則 (Liskov substitution principle, LSP)
最小知識原則 (Least knowledge principle,LKP)
介面隔離原則 (Interface segregation principle, ISP)
依賴反轉原則 (Dependency inversion principle, DIP)

若是開發的時候能遵守這六個原則,程式的品質也會大大提升。

再次節錄一段鐵哥在簡報裡放的整理:

SRP: 降低單一類別被「改變」所影響的機會
OCP: 讓主要類別不會因為「新需求」而改變
LSP: 避免繼承時子類別所造成的「行為改變」
LKP: 避免暴露過多資訊造成用戶端因流程調整而改變
ISP: 降低用戶端因為不相關介面而被改變
DIP: 避免高階程式因為低階程式改變而被迫改變

面對改變

如果看完了這些原則,也許會發現其實 SOLID 就是在說面對改變的策略
需求總是一直在變的,但是我們要怎麼在每次變化的過程中,確保改變不會帶來災難,或是埋下壞味道的種子,試著用 SOLID 去思考我們的程式碼,就可以降低改變帶來的影響。

Reference

PHP 也有 Day #19 - PHP 返樸歸真系列之從實例學設計模式 by 大澤木小鐵 (Jace Ju)​
物件導向設計原則 SOLID
SOLID:五則皆變

淺談 MVC & MVVM

為什麼要分層?

實踐單一職責原則,將工作項拆分,各單元只專注在自己的工作上。
而前後端分離的概念更甚者把資料的提供與畫面的呈現完全拆分開來。

MVC

MVC 這個名字是來源自桌面應用程式的架構,而在 web 世界裡,其實 MVC 真正的名字應是叫做 Model2,但是因為同樣都有 model、controller、view 的結構,所以名字常被誤用,久而久之,大家也都習慣稱其為 MVC,以下內容也都以 MVC 稱呼之。

MVC 是最基本且常見的分工架構,MVC 架構中將工作分成三層:

  • Model:
    負責與資料庫之間的聯繫,以及處理資料邏輯。

  • View:
    負責視覺的呈現。

  • Controller:
    在 view 與 model 之間的橋樑,屬於分配者或是控制者的角色。


Server side render 下的 MVC

在 server side render 的架構下,整個網頁包含資料和視覺呈現都是在後端處理,瀏覽器只是顯示整份 html。


Client side render 下的 MVC

現代的網頁為了讓使用者有更好的使用體驗,不要每個動作或是切換頁面都重新刷新整個頁面。誕生了client side render 的做法,一開始載入 document 時只是一個空的 html,所有的資料都是之後用動態載入,再埋入 html 裡面。

可以看到,這樣的架構下,server 是不處理有關 view 呈現的工作,都是交由前端在瀏覽器完成。而圖中那個 view 區塊事實上是可以進一步拆成前端的 MVC 或是 MVVM。


前後端大亂鬥

來看看,把前後端整合起來,會長怎樣呢?

MVVM

基於前端需求所發展出的架構。
分層如下:

  • Model:
    通常是與 server 介接取得資料,或是處理 client 端預先載入的資料。
  • View:
    負責視覺的呈現。

  • View-model:
    在 view 與 view-model 之間的橋樑,達成資料雙向綁定的效果。當 view 有動作被觸發,會自動更新 model,model 有資料更新也會自動更新 view。由於實現了雙向綁定,view-model的內容會即時呈現在 view 層,前端開發者再也不必繁複地通過 js 或是 jQuery 操縱 DOM 去更新 view,而 view 和 model 也就此解耦合,前端的工作分工更為乾淨。常見使用 MVVM 的框架有 angular, vue。

Reference

MVC與Model 2的變異與結合

前後端分離與 SPA

淺談MVVM架構

淺談MVC、MVP 和MVVM 架構模式

使用 cookie 和 session 來紀錄使用者狀態

前言

HTTP 本身是無狀態(Stateless)的通訊協定(Protocol)。
但是可以用 cookie & session 在客戶端與伺服器之間存取一些使用者狀態。

在講解如何處理使用者狀態之前,先簡單釐清 cookie & session。
cookie 是存在 client 端的,session 是存在 server 端的。

如何用 session 紀錄使用者狀態

想像去飲料店點飲料,店員發給你號碼牌,之後你可以用號碼牌來領你的飲料。
你就是 client,飲料店就是 server,號碼牌就是 session id。

換到真實的情境,server 端設定 session 開始後,會自動產生一個 session id。
這時候我們可以在 session 裡額外加上 user id。
接著在 response headers 會自動被加上下面內容,指示瀏覽器要存一個名為 PHPSESSID 的 cookie。

response header

1
Set-Cookie: PHPSESSID=8afd28b38e013478f508473db0dfd172; path=/

之後 client 端每次向 server 請求時就會夾帶著 cookie。

request header

1
Cookie: PHPSESSID=8afd28b38e013478f508473db0dfd172

server 端收到後就會根據 session id 來識別這是哪個 session 的請求,
這時候我們只要再看看 session 裡的 user id 就知道哪位 user 了。特別說明一下,中間 cookie 夾帶 session id 的傳遞都是背後會自動去執行的,server 和 client 都不用做額外的操作。

缺點:

  • session 過期前會一直佔用 server 資源

用 session 實作 login & logout

以下用 session 來實做一下使用者登入。

Login form

1
2
3
4
5
<form action="" method="post">
<input type="text" name="username" placeholder="Enter your username" required>
<input type="password" name="password" placeholder="Enter your password" required>
<input type="submit" value="Submit">
</form>

Process Login

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
session_start();

if ( ! empty( $_POST ) ) {
if ( isset( $_POST['username'] ) && isset( $_POST['password'] ) ) {
// Getting submitted user data from database
$db = new mysqli($db_host, $db_user, $db_pass, $db_name);
$sql = $db->prepare("SELECT * FROM users WHERE username = ?");
$sql->bind_param('s', $_POST['username']);
$sql->execute();
$result = $sql->get_result();
$user = $result->fetch_object();

// Verify user password and set $_SESSION
if ( password_verify( $_POST['password'], $user->password ) ) {
$_SESSION['user_id'] = $user->ID;
}
}
}
?>

Any protected page

1
2
3
4
5
6
7
8
9
10
<?php
session_start();

if ( isset( $_SESSION['user_id'] ) ) {
// Get user data if session exists
} else {
// Redirect to the login page
header("Location: https://www.yourdomain.com/login.php");
}
?>

Logout

1
2
3
<?php
session_start();
session_destroy();


紀錄使用者狀態也可以單純只使用 cookie,把 user 的身份和其他資訊都存在 cookie 裡,然後 server 就只看這個 cookie 去判斷你是誰。
當然儲存的資訊都會經過加密處理,但是還是有被破解的風險,而且 cookie 有儲存大小 4k 的限制,所以實務上有可能會 session 和 cookie-based session 配合使用。

缺點:

  • 不適合存敏感資訊

概念上 cookie 又分成兩種:

  • Persistent cookie

    就是明確指定 cookie 的存活時間,直到指定的時間為止,即便瀏覽器關閉 cookie 依然活著。

    1
    setcookie("TestCookie", $value, time() + 60 * 60 * 24);
  • Session cookie

    session cookie 則是相反,不指定存活時間,只活在這個瀏覽器的 session 期間,關閉瀏覽器後就被清除了。但是實測後發現如果在 chrome 上有勾選繼續瀏覽上次開啟的網頁(Continue where you letf off),session cookie 是不會被清除的。Ref

HTML5 新增的兩種儲存方式: local storage & session storage,下面把他們與 cookie 做一下比較。

Cookies Local Storage Session Storage
Capacity 4kb 10mb 5mb
Browsers HTML4/HTML5 HTML5 HTML5
Accessible from Any winodw Any winodw Same tab
Expires Manually set Never On tab close
Storage Location Browser and server Browser only Browser only
Sent with requests Yes No No
Restriction Same domain Same domain Same domain

Reference

Web 技術中的 Session 是什麼?

How to Build a PHP Login Form Using Sessions

cookies vs localStorage vs sessionStorage

crontab 設定

基本介紹

crontab 用來設定 Linux 上例行性工作,由 crond 提供排程服務。

crond 預設有三個地方會有執行腳本設定檔:

/etc/crontab : 一般放置系統排程
/etc/cron.d/*: 一般放置系統排程,或是自己開發的軟體所需的排程,例如 /etc/cron.d/myJob
/var/spool/cron/*: 使用者自己的 crontab 設定


管理使用權限

/etc/cron.allow:紀錄可以使用的帳號
/etc/cron.deny:紀錄不可以使用的帳號

建議擇一使用即可


操作

設定個別使用者的 crontab

編輯排程,其真正代表的是編輯 /usr/bin/crontab 這個執行檔

1
crontab -e

移除所有的 crontab 的工作內容

1
crontab -r

顯示目前排程

1
crontab -l

改變排程的執行身份

1
crontab -u

設定系統的 crontab

1
vi /etc/crontab

啟動服務

通常 crontab 設定完即啟用設定,但有些 Unix 版本如可能不會馬上啟用。
這時候需要重新啟動 crond 服務

1
systemctl restart crond

規則

小時 星期 執行身份 指令
0~59 0~23 1~31 1-12 0-6 帳號 sh myJob.sh

範例

每分鐘執行一次:輸出文字至檔案 test.txt

1
* * * * * echo 'test' >> /home/yuchi/test.txt

Reference

online crontab editor
https://crontab.guru/

https://code.kpman.cc/2015/02/11/%E5%88%A9%E7%94%A8-crontab-%E4%BE%86%E5%81%9A-Linux-%E5%9B%BA%E5%AE%9A%E6%8E%92%E7%A8%8B/

http://linux.vbird.org/linux_basic/0430cron.php