2025/03/03

Docker PHP Extension Installer

我是一位 PHP 開發者,目前許多的 production 專案也會用到 docker 了,所以套件安裝經常會在 Dockerfile 出現,在 php - Official Image | Docker Hub 上你要安裝 extension,他的範例是:

FROM php:8.2-fpm
RUN apt-get update && apt-get install -y \
		libfreetype-dev \
		libjpeg62-turbo-dev \
		libpng-dev \
	&& docker-php-ext-configure gd --with-freetype --with-jpeg \
	&& docker-php-ext-install -j$(nproc) gd

這樣寫蠻醜的也不好維護,後來找到了好用的 mlocati/docker-php-extension-installer: Easily install PHP extensions in Docker containers,安裝方式就變得很乾淨。

FROM php:7.2-cli

RUN curl -sSLf \
        -o /usr/local/bin/install-php-extensions \
        https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions && \
    chmod +x /usr/local/bin/install-php-extensions && \
    install-php-extensions gd xdebug

在 install-php-extensions 一直增加你要的東西就可以,下方還有各 PHP 有哪些支援的 extension 表格對應,可以安裝 composer 版本也很貼心。

# Install the latest version
install-php-extensions @composer
# Install the latest 1.x version
install-php-extensions @composer-1
# Install a specific version
install-php-extensions @composer-2.0.2

Force LF EOL In Git

Windows 以及 MacOS 或 Linux 的換行符號是不太一樣的,Windows 是 CRLF,Linux like 是 LF,在 Windows 使用 git 如果沒有有設定參數,pull 或 commit 時 git 有可能會幫你自己轉換行符號,這樣會導致 git 在你沒有修改任何內容的情況下判定你有更改檔案,為了避免這樣的狀況最好是設定以下的內容:

$ git config core.eol lf
$ git config core.autocrlf input

Reference

github - Force LF eol in git repo and working copy - Stack Overflow

Scoop Update Command

我在 Windows 上使用 PowerShell 搭配 scoop 來 maintain 我的常用工具已經一陣子了,由於我是更新控,所有的軟體只要出了新版本我都要安裝,scoop 可以用指令來自動偵測更新,以下是我使用的更新命令。

scoop update && scoop update * && scoop cleanup * && scoop cache rm *

指令說明

  • scoop update:更新軟體庫
  • scoop update *:更新所有可以更新的軟體
  • scoop cleanup *:將舊版本刪除
  • scoop cache rm *:將 temp 的安裝檔案清除

2025/02/27

Git Export Modified Files From Commit

我們會遇到某些案子只能用 ftp deploy,會遇到只需要 upload 你修改內容的檔案這件事情,git 有可以做到這件事的指令,git archieve,該指令讓你指定 commit 後匯出從該 commit 到最後 commit 的修改檔案。

git archive -o changes.zip HEAD $(git diff --name-only 91455ec0^ HEAD)

只要將後面的 commit hash 改成你這波修改的開始就可以將修改的內容包含原始目錄結構匯出成 changes.zip,省去每次都要大量覆蓋的問題,如果你是使用 JetBrains 的軟體,並且使用他的 git 管理介面以及 ftp 功能,你可以在一整排你要上傳的使用 Ctrl + Shift + Alt + x 叫出 ftp 上傳介面,他很聰明的就會上傳你選擇的所有 commit 的修改檔案上去,很方便,我真的覺得 JetBrains 可以考慮把他的 git 管理工具獨立成一套軟體,像是 Sourcetree 那樣可以單獨使用,真的很好用。

2025/02/12

Linux Find

之前客戶 server 遇到單一目錄檔案過大問題,使用 rm -f * 或者是砍掉整個目錄都遇到 arguments 過多問題,最終使用 find 指令解決,有在使用 Linux 的人一定知道 find 的強大,在這邊作把常用的案例濃縮筆記一下。

範例

一般

find . -type f -name "lebron*" # 搜尋 lebron 為首的檔案
find . -type f -iname "lebron*" # 搜尋 lebron 為首的檔案(不區分大小寫)
find . -type d -name "lebron*" # 搜尋 lebron 為首的目錄

時間類

參數 說明 單位 範例 說明
atime 檔案最後存取的時間 天 find /path -atime -7 找出 7 天內被存取過的檔案
ctime 檔案的 inode 最後變更時間 天 find /path -ctime +30 找出 30 天前 inode 變更的檔案(如權限修改)
mtime 檔案內容的最後修改時間 天 find /path -mtime 0 找出今天內修改過內容的檔案
amin 檔案最後存取的時間 分鐘 find /path -amin -60 找出 60 分鐘內被存取過的檔案
cmin 檔案的 inode 最後變更時間 分鐘 find /path -cmin +120 找出 120 分鐘前 inode 變更的檔案
mmin 檔案內容的最後修改時間 分鐘 find /path -mmin -30 找出 30 分鐘內修改過內容的檔案

-time

指令 查詢範圍 檔案舉例(假設今天是 2024/02/21)
-mtime +3 4 天前或更早 ✅ 2024/02/17、✅ 2024/02/16
-mtime 3 剛好 3 天前 ✅ 2024/02/18
-mtime -3 最近 3 天內(包含今天) ✅ 2024/02/21、✅ 2024/02/20、✅ 2024/02/19
-mtime 0 今天(00:00 之後) ✅ 2024/02/21(今天)

min

指令 查詢範圍 檔案舉例(假設現在是 12:00 PM)
-mmin +30 31 分鐘前或更早 ✅ 11:29 AM、✅ 11:00 AM
-mmin 30 剛好 30 分鐘前 ✅ 11:30 AM
-mmin -30 最近 30 分鐘內(含現在) ✅ 12:00 PM、✅ 11:50 AM、✅ 11:45 AM
-mmin 0 最近 1 分鐘內(剛改變) ✅ 12:00 PM

空間類

參數 說明 單位 範例 說明
-size 指定檔案大小 512-byte blocks (預設) find /path -size +100M 找出大於 100MB 的檔案
-empty 找出空檔案或空目錄 N/A find /path -empty 找出空檔案或空目錄
-maxdepth 限制搜尋深度 N/A find /path -maxdepth 2 -size +1G 限制搜尋到最多 2 層目錄內,找出大於 1GB 的檔案
-type f 限制搜尋結果為檔案 N/A find /path -type f -size -50k 找出小於 50KB 的檔案
-type d 限制搜尋結果為目錄 N/A find /path -type d -size +10M 找出超過 10MB 的目錄(通常為不常見情況)

-size 常見用法:

  • find /path -size +1G → 找出 大於 1GB 的檔案
  • find /path -size -500k → 找出 小於 500KB 的檔案
  • find /path -size 100M → 找出 剛好 100MB 的檔案
  • find /path -size +100M -size -500M → 找出大小介於 100MB ~ 500MB 之間的檔案

-size 可用單位:

  • c → bytes(例如 -size +1024c 找出大於 1024 bytes 的檔案)
  • k → KB(例如 -size -500k 找出小於 500KB 的檔案)
  • M → MB(例如 -size +10M 找出大於 10MB 的檔案)
  • G → GB(例如 -size +1G 找出大於 1GB 的檔案)

顯示

格式 說明 範例
%p 檔案的完整路徑 ./test.txt
%f 檔案的名稱(不包括路徑) test.txt
%s 檔案的大小(以 byte 為單位) 1024
%M 檔案的權限(如 ls -l 顯示) -rw-r--r--
%u 檔案的擁有者 user1
%g 檔案的群組 group1
%T@ 檔案的最後修改時間,Unix 時間戳 1592390505.000000000
%TY-%Tm-%Td 檔案的修改日期(年-月-日) 2024-02-12
%TH:%TM:%TS 檔案的修改時間(時:分:秒) 14:30:15
%p 檔案的路徑 ./folder/test.txt
%k 檔案的大小(以 1KB 為單位,四捨五入) 1
%l 檔案是符號連結時,顯示其指向的檔案路徑 ./link_to_file.txt
%c 檔案的創建時間(僅支援某些檔案系統) 2024-02-10 12:00:00

顯示檔案名稱與大小(以 KB 為單位)

find . -type f -printf "%s KB\t%p\n"
1024 KB    ./file1.txt
2048 KB    ./file2.log

顯示檔案的修改日期與名稱

find . -type f -printf "%TY-%Tm-%Td %p\n"
2024-02-12 ./file1.txt
2024-02-12 ./file2.log

顯示檔案權限、擁有者、群組、大小和名稱

find . -type f -printf "%M %u %g %8s %p\n"
-rw-r--r-- user1 group1    1024 ./file1.txt
-rw-r--r-- user2 group2    2048 ./file2.txt

執行類

-printf

指令 說明 範例 解釋
-exec 對符合條件的檔案執行指令 find /path -type f -name "*.log" -exec rm {} \; 刪除所有 .log 檔案
-exec (多個參數) 傳遞多個參數到指令 find /path -type f -name "*.txt" -exec mv {} /backup/ \; 移動 .txt 檔案到 /backup/
-exec (多檔案批次處理) 用 + 來一次傳遞多個檔案 find /path -type f -name "*.jpg" -exec ls -lh {} + 列出 .jpg 檔案詳細資訊(批次傳遞參數)
-execdir 在檔案所在目錄執行指令 find /path -type f -name "*.bak" -execdir rm {} \; 在檔案所在目錄刪除 .bak 檔案

刪除所有 .log 檔案:

find /var/log -type f -name "*.log" -exec rm {} \;

批次刪除 .tmp 檔案(避免多次執行 rm):

find /tmp -type f -name "*.tmp" -exec rm {} +

找出 .sh 檔案並修改權限:

find /scripts -type f -name "*.sh" -exec chmod +x {} \;

計算 .log 檔案總大小:

find /var/log -type f -name "*.log" -exec du -ch {} + | tail -n 1

-delete:

指令 說明 範例 解釋
-delete 刪除符合條件的檔案 find /path -type f -name "*.log" -delete 刪除所有 .log 檔案
-delete (空目錄) 刪除空目錄 find /path -type d -empty -delete 刪除所有空目錄
-delete (限制層級) 限制搜尋深度刪除 find /path -maxdepth 1 -type f -name "*.tmp" -delete 只刪除 /path 下的 .tmp 檔案,不影響子目錄

其他

參數 說明 範例 解釋
-user 搜尋指定使用者擁有的檔案 find /path -user username 找出屬於指定使用者的檔案
-group 搜尋指定群組擁有的檔案 find /path -group groupname 找出屬於指定群組的檔案
-perm 根據檔案權限搜尋檔案 find /path -perm 755 找出擁有 755 權限的檔案
-newer 搜尋修改時間在指定檔案之後的檔案 find /path -newer file1.txt 找出在 file1.txt 之後被修改過的檔案
! -newer 搜尋修改時間在指定檔案之前的檔案 find /path ! -newer file1.txt 找出在 file1.txt 之前被修改過的檔案
-maxdepth 限制搜尋的最大目錄層級 find /path -maxdepth 2 只搜尋最多 2 層目錄深度的檔案
-mindepth 限制搜尋的最小目錄層級 find /path -mindepth 2 只搜尋從第 2 層開始的檔案
-print0 輸出檔案名稱並以 null 字元結尾,用於處理空格 find /path -print0 方便與 xargs -0 搭配使用,避免檔名有空格
-0 接受 null 字元分隔的檔案名稱 find /path -print0 | xargs -0 rm 用於 xargs,確保檔名包含空格的情況下也能正確處理
-iname 不區分大小寫的檔案名稱搜尋 find /path -iname "*.jpg" 搜尋所有 .jpg 檔案,不區分大小寫
-empty 搜尋空的檔案或目錄 find /path -empty 找出所有空的檔案或目錄
-exec 執行指定的命令來處理找到的檔案 find /path -name "*.log" -exec ls -lh {} \; 對每個找到的檔案執行 ls -lh
-exec + 批次處理所有符合條件的檔案,提高效率 find /path -name "*.log" -exec ls -lh {} + 批次執行命令,將多個檔案一次傳遞給命令處理
-prune 排除特定目錄,使其不被搜尋 find /path -path "/path/exclude" -prune -o -name "*.txt" -print 排除 /path/exclude 目錄,只搜尋其他 .txt 檔案
-follow 跟隨符號連結 find /path -follow -name "*.txt" 跟隨符號連結,搜尋 .txt 檔案

2024/10/15

Asynchronous Crawler In PHP, Node.js And Python

久久就要複習一下爬蟲寫法,不然不常用就生疏了,針對三種語言寫個 async 的版本吧。

node.js

const axios = require('axios');  
const urls = [  
    'https://jsonplaceholder.typicode.com/todos/1',  
    'https://jsonplaceholder.typicode.com/todos/2',  
    'https://jsonplaceholder.typicode.com/todos/3',  
];  
  
(async () => {  
    try {  
        const requests = urls.map(url => axios.get(url));  
        const responses = await Promise.all(requests);  
  
        responses.forEach(response => {  
            console.log(response.data.id);  
        });  
    } catch (error) {  
        console.error(error.message);  
    }  
})();

Python

import aiohttp  
import asyncio  
  
urls = [  
    'https://jsonplaceholder.typicode.com/todos/1',  
    'https://jsonplaceholder.typicode.com/todos/2',  
    'https://jsonplaceholder.typicode.com/todos/3',  
]  
  
  
async def fetch_url(session, url):  
    async with session.get(url) as response:  
        return await response.json()  
  
  
async def main():  
    async with aiohttp.ClientSession() as session:  
        tasks = [fetch_url(session, url) for url in urls]  
        responses = await asyncio.gather(*tasks)  
  
        for responses in responses:  
            print(responses.get('id'))  
  
  
if __name__ == '__main__':  
    asyncio.run(main())

PHP

<?php

use GuzzleHttp\Client;
use GuzzleHttp\Promise\Utils;

include "vendor/autoload.php";

$urls = [
    'https://jsonplaceholder.typicode.com/todos/1',
    'https://jsonplaceholder.typicode.com/todos/2',
    'https://jsonplaceholder.typicode.com/todos/3',
];

$client = new Client([
    'verify' => false,
]);
$promises = [];

foreach ($urls as $url) {
    $promises[] = $client->getAsync($url);
}

$results = Utils::all($promises)->wait();

foreach ($results as $result) {
    echo json_decode($result->getBody()->getContents(), true)['id'], PHP_EOL;
}

2024/10/09

(G)I-DLE 2024 Concert In Taipei

我蠻常看演唱會的,但這是我第一次看韓團的演唱會,太喜歡 (G)I-DLE 這個團體了,歌好聽,五個團員又跟瘋子一樣好笑,其中我的本命是 Minnie,迷人的雙眼,有磁性的聲音,親切的外表,完全無法招架。

應該很多人都是從這部影片入坑的,Minnie 非常有語言天分,其他團員雖然也算努力學中文,但 Minnie 講起中文幾乎沒有口音,而且有正確的邏輯理解,這段跟屬於中國人的雨琦吵架居然用邏輯輾壓 😙

她那磁性十足的嗓音我個人覺得她 solo 是完全沒問題的,真的是太可愛了,難能可貴的是,她家在泰國是有錢的家族,(G)I-DLE Minnie竟是隱藏版泰國公主?,有一集韓綜有去拍她家,跟皇宮差不多,但接觸過她的人都說她沒有驕氣,漂亮又可愛的代表。

入口

費盡九牛二虎之力搶到票,2024/10/06 終於讓我一償夙願可以看到他們現場表演。

Taco

由於演唱會是 17:30 開始,所以先吃點東西墊墊肚子,小巨蛋裡面是不可以吃東西的。

演唱會內部

進到演唱會現場超興奮的,我參加過那麼多演唱會,第一次遇到光是看 MV 就全場大合唱的情況,真的超級 high。

我錄了幾首我特別喜歡的歌的片段,大部分的時間都是專心用雙眼雙耳享受現場表演,他們現場實在是太迷人了,由於團內有兩位會講中文的人,跟現場的人互動環節滿點,Yuqi 跟舒華又很搞笑,聊天環節笑料不斷,我上一次看的演唱會是鄭中基,鄭的情歌居多,所以整場大部分都是專心聽歌,冷靜感動,但這場是從頭 high 到尾,因為他們可以帶動氣氛的歌太多了,大部分時間都處於爆裂爽的情況,非常非常的好玩。

這是我聽過這麼多場演唱會以來第一次有結束後不想離開的情緒,就很捨不得,坐我妹旁邊的歌迷還哭了,大家都很依依不捨吧,中間有穿插幾個換裝墊檔的影片,其中一個影片是用 QA 問每個團員一些事情,讓我印象深刻的是,當問到娟總 (G)I-DLE 的演唱會代表什麼時,娟總回答:2024 最開心的一天,我無比的贊同,那天我真的非常非常非常的開心,下次他們再來開演唱會的話,我一定會買下面的座位,我想要得到跟他們互動的機會,真的太喜歡他們了。