先前有介紹過一些 Linux 常用的指令(cd, ls, cat, cal, chmod…),大家可以回去看看唷~
大家好,相信許多人在學習程式設計到一個階段之後,總會去面臨到要使用 Linux 系統的機會。而使用 Linux 系統最重要的技能就是懂得使用終端機(terminal)來進行各種操作。
然而撇去看到一堆黑底白字看起來很可怕的畫面之外,terminal 令人害怕的其中一個點是會看到一堆奇怪的語法而不懂得那是什麼意思,還有可能因為下錯指令而對電腦造成難以逆轉的後果。
為了讓大家稍微比較了解 terminal 究竟怎麼使用,今天就來跟大家簡介一下所謂的 terminal,或者更精確的說是 Linux 系統中的命令列介面(command-line interface,CLI)—— shell script 的基本概念。
Why terminal?
實際上,所謂的終端機 terminal,正確來說是一種文字使用者介面,望文生義可知就是「使用文字和使用者互動的介面」。想像一下當我們今天要和一個人溝通時,最主要的方式就是使用語言向對方傳遞訊息,而今天如果將這種語言的對象變成機器,來對電腦進行溝通,就是文字使用者介面,簡言之就是對著電腦說話的意思。而由於對機器說的「話」通常都是一個又一個的「命令」因此通常稱之為「命令列介面」,常常也會被叫做 command-line 或 CLI。與之相對的則是圖形使用者介面(graphical user interface,GUI),也就是平常各位用滑鼠操作的、可以點擊並看到畫面的使用者介面。
而在電腦的作業系統中,為了讓電腦「聽得懂我們要它做什麼」,就需要一種語言來向電腦下指令。在 Linux (與 MacOS)系統中,命令列介面叫做「Unix shell」,而其構成的語言則常稱為「shell language」或 「shell script」。
Script?
這邊簡要說一下為什麼會叫做「script」。如果有寫過 Python 或 JavaScript 等語言的人應該體驗過,你只要對電腦輸入一行指令,電腦馬上就根據這行指令做事的感覺,這種程式語言是一個口令一個動作的,就像是按照你設計的劇本手稿去演戲,因此會叫「手稿語言(script language)」。
Shell script basic
這邊先假設大家對於多數程式語言已經有一定的基礎概念,知道「變數」、「函數」等基礎概念,僅提及 shell 語言和一般程式語言的差異,方便大家去理解 shell script 究竟在說些什麼。
function v.s command
在一般程式語言中,對於一串特定的指令動作,我們可以將其包裝成「函數(function)」來執行。執行時只要輸入一些參數,就可以執行一些動作,並得到其結果。以下以 Python、JavaScript 與 C++ 舉例:
# Python
def add(x, y):
return x + y
# ... clearer syntax
def add(x: int, y: int) -> int:
return x + y
// JavaScript
function add(x, y) {
return x + y;
}
// C++
int add(int x, int y) {
return x + y;
}
而要使用這個函數,在這些程式語言基本上都是使用以下語法來表示(當然個別語言在變數與語法處理上會有些為差異):
result = add(x, y);
然而,在 shell 語言中,要求電腦做系列動作的方式會比較接近「命令」,語法上也比較接近人類語言的祈使句(下命令用的句子)而非數學上的函數
祈使句像是:
> Take it to me later.
> 拿包裹給我!當然這邊是以英文的概念去設計的
因此為了比擬祈使句,各位可以觀察一下
HUMAN: take it to me later
SHELL: ls my_folder > output.log &
動作 對象 導向 目的地 條件
在這邊,「動作」我們可以想像成一系列小步驟的組合,因此「函數」跟「命令」的概念在這邊就可以對應上。後面的東西可以很複雜,但最直白的就想成他們都是輸入的參數就可以了(因此也不一定會有,有多少個也不一定)。
到此,我們把同樣要把 x 跟 y 相加這件事用程式語言與 shell 語言呈現就會變成
# programming languages,
# e.g. Python, C++, JavaScript
add(x, y);
# shell languages,
# e.g.. sh, bash, zsh, fish
add $x $y
variable v.s argument
唯一不同的是,為了區分「變數」與「命令」(簡單說就是「名詞」跟「動詞」啦!),「變數」需要在名稱前面加上 $
表示這是一個變數!什麼意思呢?請看下面的例子:
在學程式語言時大家一定會先碰過所謂的「hello world」,也就是起碼要在電腦上可以印出大家想寫的句子,才會繼續後面的學習。那在 shell 語言中的這個等同 C++ 的「cout」、Python 的「print」的指令叫做「echo」,用法也非常直觀
# (In C: )
# printf("Hello world\n");
# (In C++: )
# std::cout << "Hello world" << std::endl;
# (In Python: )
# print("Hello world")
# (In JavaScript: )
# console.log("Hello world");
# (In Java: )
# System.out.println("Hello world");
echo Hello world
# ^^^^^^^^^^^ That's it!
就這麼直白,但問題是,如果今天這個 world 是一個「變數」的名字,那怎麼區分是指「world」這個字串本身還是「world」存的東西呢?於是 $
的作用便體現出來了!
world=all_over_the_world
echo Hello world
# Output: Hello world
echo Hello $world
# Output: Hello all_over_the_world
# ^^^^^^^^^^^^^^^^^^ 這邊是 world 變數的內容!
然而,這邊有一個尷尬的地方是,因為是用類似於人類語言的語法去設計的,當初 shell 的變數之間會是用空白去分隔(在多數程式語言會是用逗號 ,
去分隔),因此如果今天變數的內容中間有空白怎麼辦呢?在 shell 語言的做法是用引號去括起來,如下
world="all over the world"
echo Hello $world
# Output: Hello all over the world
至於要用單引號還是雙引號呢?這邊如果對「字串插補(string interpolation)」有概念的話,應該會比較好理解。簡單說,單引號中間的東西就是純字串(換句話說,字串中間的每個字元都不會有特殊含義),雙引號則只是單純將有空白的字串包起來,但都保留其原本意義。舉例如下
#!/bin/sh
name=Lee-Hom
full_name1='Lee-Hom Wang'
full_name2="Lee-Hom Wang"
# 雙引號會保留 `$` 的原本意義
full_name3="$name Wang"
# 單引號是純字串,`$` 就是 `$`
full_name4='$name Wang'
echo '單引號'
echo $full_name1 # Output: Lee-Hom Wang
echo '雙引號'
echo $full_name2 # Output: Lee-Hom Wang
echo '雙引號會保留 `$` 的原本意義'
echo $full_name3 # Output: Lee-Hom Wang
# $name ^^^^^^^
echo '單引號是純字串,`$` 就是 `$`'
echo $full_name4 # Output: $name Wang
# "$name" literally <--=====
name1=Chien-Ming
# 如果有變數名字邊界不明可能會混淆的情況,建議使用 `{}`
echo $name # Output: Lee-Hom
echo $name 1 # Output: Lee-Hom 1
# $name ^^^^^^^
echo $name1 # Output: Chien-Ming
# $name1 ^^^^^^^^^^
echo ${name} # Output: Lee-Hom
# $name ^^^^^^^
echo ${name}1 # Output: Lee-Hom1
# $name ^^^^^^^
echo ${name1} # Output: Chien-Ming
# $name1 ^^^^^^^^^^
echo ${full_name1} # Output: Lee-Hom Wang
echo "${full_name1}" # Output: Lee-Hom Wang
以上,對於 shell script 最基本的語法「命令」與「參數」以及參數的第一步先講解到此,大家可以打開 terminal 來玩玩看了!
其實還有很多(光剛剛那個變數就有不少可以提及的了),另外其與 Windows 系統和其他程式語言的愛恨情仇更是一言難盡,請各位敬請期待!