- Bash 可作為 shell 和命令解釋器,可讓您使用簡單的腳本自動執行 GNU/Linux 中的常見任務。
- 這些腳本基於參數、變數、陣列、控制結構和函數,並大量使用外部命令。
- 文件和文字的重定向、管道和測試運算符使您能夠建立強大的系統管理工具。
- 正確使用退出程式碼、環境變數和函數,可以使 Bash 腳本具有可重複使用性、健全性和易於整合性。
如果你使用 GNU/Linux,遲早都需要管理終端機和 Bash。 掌握一本優秀的 Bash 手冊 它可以幫助您自動化任務,更好地了解系統,並停止像永遠處於「新手模式」一樣手動完成所有事情。
在本綜合指南中,您將找到 對 Bash 腳本語言的完整而實用的解釋從它是什麼以及如何在命令列中使用它,到如何編寫包含變數、陣列、循環、函數、錯誤處理、重定向、管道的腳本,以及你明天就可以在你的機器上使用的實際範例。
Bash是什麼?它究竟是用來做什麼的?
Bash(Bourne Again SHELL)是 一個用作命令解釋器的 Unix shell它是位於你和 Linux 核心之間的層:你輸入命令,Bash 解釋並執行這些命令,並將結果返回到螢幕上。
這個 shell 是大多數流行的 GNU/Linux 發行版(如 Debian 或 Ubuntu)的預設 shell,macOS 上也有。 Bash 是理想的入門程式語言。 如果您已經了解基本的控制台命令,並且想要自動化常見任務:備份、清理臨時檔案、報告、小型選單等。
把Bash想像成相當簡單的結構化語言, 高度注重系統管理它利用了所有現有的系統命令,例如 awk 命令Bash 腳本無需重複發明輪子,而是協調指令、重新導向和條件,讓系統為你完成工作。
什麼是 Bash 腳本?如何編寫 Bash 腳本?
Bash 腳本只不過是… 一個包含一系列指令的文字文件 它們在 shell 中按順序運作。隨著時間的推移,它們會逐漸發展成類似實際程序的東西:它們使用循環、函數、變數等等,但所有內容仍然是純文字。
Linux 辨識一個檔案是否為執行腳本,有三個關鍵點: 指定正確的 shell,授予執行權限,並為其命名合理。,通常帶有擴展名 .sh.
Shebang:指定要使用的解譯器
檔案的第一行必須指定要使用的解釋器的路徑。在 Bash 中,通常是:
#!/bin/bash
這個順序 #! 就是所謂的 家當 (例如 hashbang、shabang 等)。當你運行該文件時 ./script.sh系統讀取第一行程式碼,就知道該啟動什麼程式。 /bin/bash 思考接下來會發生什麼事。
如果要逐行偵錯腳本,可以新增該選項。 -x al shebang 以便 Bash 顯示它正在執行的內容:
#!/bin/bash -x
執行權限和腳本名稱
文件寫入完成後,必須將其標記為可執行檔。通常使用以下方法:
chmod 755 nombre_del_script.sh
或者更直接地說:
chmod +x nombre_del_script.sh
關於名稱,系統不要求使用任何擴展名,但是 強烈建議使用 .sh 透過視覺辨識出這是一個腳本如果從終端機運行它,並且您位於同一目錄下,它將啟動並執行以下命令: ./nombre_del_script.sh.
請記住,在 Bash 腳本中, 角色背後的一切 # 這被視為一條評論除了 shebang 行的第一行。您可以註解掉整行,也可以在語句末尾新增註解。
你的第一個 Bash 腳本編寫指南(逐步教學)
在 Linux 系統中,您可以使用任何文字編輯器來編輯腳本:從圖形編輯器(例如 VSCode、Atom 等)到控制台工具,例如… nano、vi 或 vim重要的是,你要將文件儲存為純文字格式,不要包含任何特殊字元。
我們將創建一個非常簡單的腳本,名為 hola.sh 例如,使用 vim:
vim hola.sh
在文件中,你只需寫入類似這樣的簡單內容:
#!/bin/bashecho "Hola. Soy tu primer script Bash"
這裡我們使用指令 echo 在螢幕上顯示文字。 也有 printf這樣一來,您可以更精確地控制輸出格式,對於對齊列或格式化數字非常有用。
你可以儲存檔案(例如,在 Vim 中使用以下命令)。 ESC 接下來 :wq然後授予它執行權限:
chmod 755 hola.sh
現在您可以從終端機嘗試,方法是導航到檔案目錄並執行以下命令:
./hola.sh
你應該在螢幕上看到你在腳本中定義的短語,這表示基本流程(shebang + 權限 + 執行)已正確配置。 Bash 正確解析了您的檔案。.
位置參數和移位命令
腳本在以下情況通常很有用 它們接受來自命令列的參數。Bash 提供了特殊的變數來處理這些位置參數,而無需手動解析。
腳本中的一些重要參數包括:
$#:接收到的參數數量。$0腳本本身的名稱(和路徑)。$1 ... $9參數值從 1 到 9。${N}:位置 N 的參數,當數量超過 9 時很有用。$*:所有參數(除$0) 作為單一字串。$@:所有參數都以單字「數組」的形式表示。$$:執行腳本的進程的進程 ID。$?:最後執行的命令的退出代碼。
當您想要處理以下類型的參數時 --opcion valor 按任意順序使用該命令變得非常方便 shift這會將參數向左移動。
想像一下,一開始你擁有 $1=UNO y $2=DOS如果您打電話 shift, $1 它的價值恢復到了先前的價值。 $2 以及舊價值 $1 它丟失了。這樣你就可以在瀏覽命令列時「使用」參數。
帶有 shift 和命名參數的範例腳本
一種非常典型的模式是提供諸如此類的選項。 --nombre y --apellido 按任意順序使用循環處理它們。 while 隨著 case y shift結構大致如下:
#!/bin/bash
# USO: ./nombre-apellido.sh --nombre NOMBRE --apellido APELLIDO
while ]
do
case "$1" in
-n|--nombre)
shift
nombre="$1"
shift
;;
-a|--apellido)
shift
apellido="$1"
shift
;;
*)
# parámetro no esperado, lo saltamos
shift
;;
esac
done
echo "Tu Nombre es: $nombre y tu Apellido es: $apellido"
當以如下方式運行腳本 ./nombre-apellido.sh --nombre Luis --apellido Gutierrez 或者,透過顛倒參數順序,輸出結果將相同,因為 循環負責將每個值分配給其對應的位置。 無論初始位置如何。
Bash 中的變數:全域變數、局部變數和文字變數
在 Bash 中,你可以定義變數而無需宣告類型。它們是 無類型變數它們可以包含文字、數字,甚至是數組,解釋器會處理它們。
賦值的基本方法是:
VAR=5
VAR=texto
VAR='cadena literal'
VAR="cadena con variables"
使用單引號時,內容將被視為純文字;使用雙引號時, Bash 可以解釋特殊變數和序列。 在鏈條內部。
例如,在腳本中你可以這樣寫:
nombre=Luis
CALLE="Calle Larga"
Despacho=401
之後,要讀取它的值,需要在它前面加上符號。 $ 變數名例如 $nombre請注意,Bash 區分大小寫,所以 CALLE y calle 它們不是同一個變數。
將命令輸出捕捉到變數中
Bash 最強大的功能之一是它允許 將命令結果儲存在變數中毫不費力。有兩種等效的語法:
VAR=$(comando)
VAR=`comando`
例如,要保存 $Usuario 目前系統使用者可以使用:
Usuario=$(whoami)
如果要同時收集標準輸出和錯誤訊息,可以進行重定向。 stderr 至 stdout 在命令替換中,以便 所有變數保持不變:
Salida=$(comando 2>&1)
將變數和文字連接起來也是常見的做法:
VarB="$V1 texto1 $V2 texto2"
或者,可以嘗試調整位置參數來建立新的字串,例如:
SORTEMARAP="$3 $2 $1"
我會把它儲存起來 SORTEMARAP 前三個參數依相反順序排列.
單引號與雙引號
單引號 ' 它們阻礙了任何形式的解讀: 您在裡面寫的任何內容都會原樣保存。因此,如果你這樣做:
NONO='$3 $2 $1'
在變數中 NONO 真的會得救 $3 $2 $1並非這些參數的具體值。但是,使用雙引號時,變數會被展開。
一個典型的解釋型連接範例是:
VarA="En un lugar"
VarB='de la Mancha'
VarC="de cuyo nombre no quiero"
VarD=acordarme
TEXTO="$VarA $VarB $VarC $VarD"
通過做 echo "$TEXTO" 你會得到一個完整的句子,證明: 變數用雙引號括起來進行替換。但不能放在單引號內。
Bash 中的陣列及其處理方法
Bash 支援索引數組(現代版本也支援關聯數組),這在您需要時非常有用。 處理證券集合 無需建立上千個鬆散變數。
它們可以透過多種方式申報:
declare ARRAYotypeset ARRAY建立一個可容納 9 個元素的陣列。declare -a Colores:聲明一個大小不固定的陣列。Frutas=(Pera Manzana Platano):用這些元素初始化數組。
若要寫入特定索引,請使用下列語法: ARRAY=valor記住,這些指數 它們從 0 開始,一直到 n-1。 如果數組有 n 個連續元素。
例如:
Marca="Tranqui-Cola"
COCHE="Seat"
COCHE="Opel"
讀取數值時使用略有不同的表示法,即使用花括號:
${ARRAY}:位置 n 的元素。${ARRAY}數組中的所有元素。${#ARRAY}儲存的元素數量。${!ARRAY}:目前正在使用的索引清單。
以下是一個查看特定元素的典型範例:
Frutas=(Pera Manzana Platano)
echo ${Frutas} # Platano
列出所有內容:
echo ${Frutas} # Pera Manzana Platano
請記住,這是有可能的。 在數組中留下“空位” 如果只分配某些索引,元素數量和使用的最高索引可能不匹配。因此,人們對……很感興趣。 ${!ARRAY} 僅掃描現有索引。
遍歷數組並將它們連接起來
為了遍歷數組而不遇到不存在的索引,通常使用循環。 for 關於索引列表:
for i in ${!ARRAY}
do
echo "ARRAY = ${ARRAY}"
done
您也可以將兩個陣列的內容合併到第三個陣列中,例如:
Unix=('SCO' 'HP-UX' 'IRIX' 'XENIX')
Linux=('Debian' 'Suse' 'RedHat')
NIX=("${Unix}" "${Linux}")
這就是獲取數組的方法 包含兩個原始數組的所有元素能夠稍後列出 ${NIX} 一次性看到它們。
Bash腳本中的控制結構
與任何腳本語言一樣,Bash 也提供 條件語句、迴圈語句和多重選擇結構 從而可以控制執行流程。
基本形式 if 聲音:
if
then
comandos
fiif
then
comandos
else
comandos_alternativos
fiif
then
comandos
elif
then
otros_comandos
else
comandos_por_defecto
fi
尊重他人很重要。 比較中的空格 括號內: 沒錯,但是如果沒有空格,Bash 將無法正確解析。
類型的循環 for 它們有兩種常見變異:
- 清單樣式:
for var in lista; do ...; done - C 型:
for ((i=0; i<5; i++)); do ...; done
加 for, 你有 while (條件為真時) until (它會一直運行直到滿足條件),對…很有用 條件控制循環代替計數器控制循環.
對於有很多選項的情況,你可以使用 case:
case $VARIABLE in
valor1)
comandos_opcion1
;;
valor2|valor3|valor4)
comandos_para_varios_valores
;;
*)
comandos_por_defecto
;;
esac
如果您想要一個簡潔小巧的互動式選單, select 它會自動產生一個編號的選項清單。:
PS3="Escoja una opcion: "
select Opcion in "Actualizar" "Listar" "Salir"
do
echo "Ha elegido: $Opcion"
if ; then
break
fi
done
若要退出循環或跳過迭代,您可以使用:
break:退出當前循環。break N退出 N 個嵌套循環。continue:跳到下一次循環迭代,省略目前迭代中的其餘命令。
邏輯運算子、算術運算子和文字運算符
在條件語句中,您可以將表達式與…組合使用 例如邏輯運算符 && (和) || (要么)通常在括號外:
if ||
then
# algo
fi
對於數值計算,Bash 支援標準運算子:加法。 +, 停留 -, 乘法 *, 分配 /, 模組 %, 力量 **, 增加 ++ 並減少 --評估它們的常用方法有:
$((operacion))$(較舊,但仍受支援)
簡單範例:
echo $((2+5)) # 7
echo $ # 3
i=1; echo $ # 2
echo $ # 9
文字比較中會用到如下運算子:
==(平等的)!=(清楚的)>y<按詞彙順序排列(註:在最好還是躲開它們)-z檢查字串是否為空-n檢查長度是否不為零
在數值比較中,通常優先使用特定的運算子:
-eq(平等的)-gt(大於)-ge(大於或等於)-lt(少於)-le(小於或等於)-ne(清楚的)
Bash 也允許你這樣做 鏈上操作 使用語法 ${}。 例如:
${#cadena}: 長度。${cadena:N}:從位置 N 開始的子字串。${cadena:N:M}:從 N 開始的 M 個字元。${cadena#texto}y${cadena%texto}:刪除符合的前綴或後綴。${cadena/texto1/texto2}替換第一個匹配項。${cadena//texto1/texto2}替換所有匹配項。${cadena/#texto1/texto2}o${cadena/%texto1/texto2}:僅替換開頭或結尾。
檢查檔案和權限
在管理腳本中,經常需要知道某個檔案是否存在、是否為目錄、是否具有權限等等。這些腳本就是用來做這些的。 文件的特定測試運算符 通常在以下範圍內使用 o ]:
-e:存在(檔案或目錄)。-s存在且不為空。-d存在且是一個目錄。-f存在且為普通文件。-r已獲得閱讀權限。-w:擁有寫作許可。-x:可執行(或如果是目錄則可存取)。-O目前使用者即為所有者。-G:屬於目前使用者群組。-nt比另一個文件更新。-ot比另一個文件更舊。
若要變更權限,請使用下列命令 chmod (切換模式)無論是在數字模式下(例如 755)或像徵性的(如 +x 新增執行權限)。若要變更所有者用戶,您必須 chown這對於部署和維護腳本非常有用,而且對於… 使用 rsync 同步 您可以實現系統間高效資料傳輸的自動化。
重定向、/dev/* 和管道
Linux 中的每個程式都使用三種基本檔案描述符: 標準輸入(stdin,0)、標準輸出(stdout,1)和錯誤輸出(stderr,2)Bash 允許您將它們中的任何一個重新導向到特殊檔案或裝置。
最常見的重定向運算子有:
> fich:將 stdout(如果在某些 shell 中直接使用,有時也會將 stderr)傳送到文件,覆蓋其內容。>> fich:將輸出加入到文件末尾。1> fich:僅重定向標準輸出。2> fich:僅重定向 stderr。2>&1:將 stderr 傳送到 stdout 所在的位置。< fich:使用文件內容作為命令的標準輸入。
例如,要列出目錄的內容並將結果儲存到檔案中,可以使用以下命令:
ls -1 /tmp/Carpeta1 > /tmp/lista_ficheros.txt
如果要區分正常輸出和錯誤輸出,可以將每個資料流分別放到不同的檔案中。 它大大簡化了調試和日誌記錄。:
ls -1 /tmp/Carpeta1 1>/tmp/lista_ficheros.txt 2>/tmp/errores.txt
如果您完全不關心錯誤訊息,您可以隨時將它們發送到 /dev/nullUnix「黑洞」:
ls -1 /tmp/Carpeta1 1>/tmp/lista_ficheros.txt 2>/dev/null
一種廣泛使用的模式 完全靜音命令的輸出 是:
comando >/dev/null 2>&1
關於腳本中使用的特殊設備,值得了解:
/dev/null:丟棄所有已寫好的內容。/dev/randomy/dev/urandom產生偽隨機數。/dev/zero:產生空位元組。/dev/full始終為“滿”,可用於檢查拼字錯誤。
另一方面,營運商 | 創建一個 行程之間的管道將一個命令的輸出作為下一個命令的輸入。簡單的例子有:
ls -1 | sort # orden alfabético ascendente
ls -1 | sort -r # orden inverso
您也可以將多個管道串聯起來,例如,透過組合多個管道來獲取當前螢幕解析度。 xrandr, grep y awk將多個過濾器串聯起來是一種非常強大的方法,只需幾行程式碼即可建立自訂工具。
Bash 中的函數與變數作用域
當你在腳本中多次重複相同的邏輯時,很自然的做法是將其封裝到一個函數中。在 Bash 中,函數主要有兩種定義方式,但是… 兩種情況下,正文都放在括號裡。:
function NombreFuncion { comandos; }NombreFuncion() { comandos; }
函數不會自動執行:它們必須在腳本的其他位置按名稱呼叫。在函數內部,它們接收的參數以如下方式存取: $1, $2等等,就像在劇本裡一樣。
關於變數的作用域,預設情況下,任何在函數外部定義的變數都是外部變數。 整體 並且可以在函數內部讀取和修改它。如果希望變數僅存在於函數內部,則必須使用 `export` 宣告它。 local:
function AlgoPasa {
local UNO="Texto uno"
DOS="Texto dos"
echo "Dentro de la función UNO=$UNO y DOS=$DOS"
}
在這個例子中, UNO 當地 進入該函數後,該函數會在退出時消失,而 DOS 它是全局性的,函數內部的任何更改都會保留下來,這一點需要注意,以避免無意中覆蓋值。
Bash 中的退出碼與返回碼
Linux 中的每個程式最終都會傳回一個值。 退出代碼按照慣例, 0 這意味著一切順利,任何非零值都表示存在某種錯誤。在 Bash 中,此資訊可透過特殊變數取得。 $?.
在腳本中,建議以一個結尾。 exit num 與已發生的事情保持一致,以便其他腳本或程式能夠相應地採取行動。一個典型的例子是:
#!/bin/bash
RUTA="$1"
ls -l "$RUTA" 2>/dev/null
CodError=$?
if ; then
echo "Todo correcto"
else
echo "Atención: se produjo algún error"
fi
exit $CodError
這些功能還可以指示它們運作得好還是不好,但在這種情況下,請使用以下方式: return num 而不是 exit. return 它只允許數字,因此如果您需要傳回文字、陣列或其他類型的數據,則必須使用變數(通常是全域變數)來實現。
一個簡單的例子:一個函數,它接受兩個單字並將它們合併到一個名為 `<word>` 的全域變數中。 Cadena主腳本檢查是否已傳遞兩個參數,如果沒有,則傳回錯誤。
Bash腳本的實際範例
為了確保以上所有內容不會停留在純粹的理論層面,查看實際的劇本非常有用。 它們解決 GNU/Linux 系統中的日常問題。以下列舉幾個例子來說明不同的概念。
逐行讀取文件
一種經典的模式是逐行處理文字檔案的內容:
#!/bin/bash
FICHERO="$1"
if ; then
while read LINEA
do
echo "$LINEA"
done < "$FICHERO"
exit 0
else
echo "Debe indicar un fichero existente"
exit 1
fi
這種結構允許你使用循環對每一行應用指令、篩選資料、產生報告等等。 while read 輸入重定向提供.
將桌面螢幕錄製到 .avi 文件
使用類似的工具 ffmpeg, xrandr y zenity (對於圖形對話框)可以設定一個腳本來啟動和停止錄製主桌面,並偵測解析度和顯示器數量:
#!/bin/bash
function Inicia {
FICH=$(tempfile --suffix=.avi)
NUMMONITORES=$(xrandr | grep '*' | wc -l)
RESOLUCION=$(xrandr | grep current | awk -F "," '{print $2}' | awk -v NP=$NUMMONITORES '{print $2/NP"x"$4}')
ffmpeg -y -f x11grab -s "$RESOLUCION" -r 25 -i :0.0 "$FICH" -loglevel quiet &
}
function Finaliza {
killall -9 ffmpeg
FICH=$(ls -1tr /tmp/*avi | tail -1)
echo "Vídeo guardado en: $FICH"
}
function Ejecuta {
EJECUTANDOSE=$(ps aux | grep -i ffmpeg | grep -v grep | wc -l | awk '{print $1}')
if ; then
Finaliza
else
Inicia
fi
}
Ejecuta
聯合使用 函數、全域變數、管道和外部命令所有操作均由 Bash 控制。
用於安全或便利地設定 sudo 的腳本
在某些情況下,將使用者自動新增至配置可能很有用。 sudo使用 Bash,您可以產生文件。 /etc/sudoers.d/ 用合適的方式複製它 su:
#!/bin/bash
# Activar sudo pidiendo contraseña en cada uso
echo "Debe proporcionar la clave de root cuando se le solicite"
echo "$USER ALL=(ALL:ALL) ALL" > /tmp/autorizado_$USER
su -c "cp /tmp/autorizado_* /etc/sudoers.d/."
而對於「便捷」模式,使用該模式時無需密碼。 sudo只需更改寫入臨時文件的那一行:
echo "$USER ALL=(ALL) NOPASSWD: ALL" > /tmp/autorizado_$USER
這些類型的腳本展示了 Bash 的威力和危險程度如何? 因為它會接觸到系統的敏感部位,所以最好在了解相關知識後再使用。
使用 tput 和關聯數組在終端機中實現“雪花”效果
除了嚴肅的任務之外,Bash 也非常適合在終端機中使用視覺元素。 tput 要移動遊標和顏色,再加上用於追蹤每片雪花位置的關聯數組,您可以模擬下雪:
#!/bin/bash
LINEAS=$(tput lines)
COLUMNAS=$(tput cols)
declare -A CopoDeNieve
declare -A UltimosCopos
clear
mover_copo() {
i="$1"
if }" ] || }" = "$LINEAS" ]; then
CopoDeNieve=0
else
if }" ]; then
printf "\033}" "$i"
fi
fi
printf "\033}" "$i"
UltimosCopos=${CopoDeNieve}
CopoDeNieve=$((${CopoDeNieve}+1))
}
while :
do
i=$(($RANDOM % $COLUMNAS))
mover_copo "$i"
for x in "${!UltimosCopos}"; do
mover_copo "$x"
done
sleep 0.1
done
該腳本結合了 關聯數組、無限迴圈、環境變數等 $RANDOM 和 ANSI 序列 完全透過純 Bash 來操控遊標。
命令列中的 Bash:常用基本指令
除了腳本語言之外,你每天都會頻繁使用互動式 shell。一些值得掌握的基本指令包括: pwd、cd、ls、tree、mkdir、touch、cp、mv、rm、less、cat、head、tail、hexdump 和 grep.
例如, pwd 您可以看到目前目錄的完整路徑,以及 cd 你更改資料夾,並且 ls 列出內容。該命令 tree 它會顯示目錄和檔案的遞歸樹狀結構,非常方便了解專案的整體情況。
為了創造新事物,你使用 mkdir (目錄)和 touch (文件)。 cp 複製文件, mv 移動或重命名它們,並且 rm 他把它們刪除了。 務必小心 rm -r y rm -rf因為它們可以刪除整個目錄,而且沒有恢復的辦法。
要查看文件, less 它非常方便,因為它允許你使用鍵盤進行導航,而 cat 直接把所有內容都倒出來。 head y tail 它們分別顯示檔案的開頭和結尾,並接受該選項 -n 指定要顯示的行數。
如果你從事二進位或記憶體結構相關的工作, hexdump 它允許您以十六進制形式查看內容。要在文件中搜尋文本, grep 它是明星工具並與管道完美融合:例如, cat *.c | grep sleep 它會顯示它出現的所有行。 sleep 在你的 C 原始碼中。查看系統日誌也需要了解這一點。 journalctl.
環境變數和 .bashrc 文件
環境變數是以下值: 它們會被載入到你的 shell 會話中,並影響程式和腳本的行為。你可以用…看到它們 env o printenv並以這種格式顯示 NOMBRE=valor.
要定義一個臨時環境變量,只需執行以下操作:
export MI_VARIABLE='aguante sistemas operativos'
在該會話(及其子會話)持續期間,您將能夠訪問 $MI_VARIABLE但是,如果您關閉終端,它就會消失。要讓它在每個新的互動式 shell 中自動加載,您可以新增以下配置: export 對應你的結尾 文件 ~/.bashrc 並打開一個新的終端。
典型流程如下:返回主螢幕 cd ~, 編輯 .bashrc 同 nano .bashrc 或類似的意思,在結尾處添加一些內容。 export MI_VARIABLE='aguante sisop'儲存並關閉。從那以後,每次打開新的控制台時, 無需進行任何其他操作,即可使用該變數。.
總而言之,掌握 Bash 作為 shell 和腳本語言,可以讓你對任何 GNU/Linux 系統進行非常精細的控制:你可以自動化從最簡單的任務到複雜的部署流程的一切操作,管理日誌,監控服務,為其他用戶構建小型菜單,最終讓機器為你工作,而不是一遍又一遍地手動重複命令。
對字節世界和一般技術充滿熱情的作家。我喜歡透過寫作分享我的知識,這就是我在這個部落格中要做的,向您展示有關小工具、軟體、硬體、技術趨勢等的所有最有趣的事情。我的目標是幫助您以簡單有趣的方式暢遊數位世界。