Advanced Shell Programming

Unix 之優缺點

學習 Unix Shell Programing 之方法

Design Objective and Procedure

Shell Language Constructs

指令之執行步驟 (Command Line Evaluation)

未定義變數 (Undefined Variable)

Variables Type

Variable Naming Convention

Variable Assignment

變數之展開 (Variable Substitution)

設定變數的預設值 (Default Values in Variable Expansion)

變數展開的POSIX擴充功能-字串與算數運算

變數展開的字串處理功能

檔案名稱的字串處理

變數展開的算數計算功能 (Arithmetic Expansion)

Arithmetic Statement in KSH

String Concatenation

Environment

特殊變數

Special Variables for Position Parameters $* and $@

特殊符號 (Special Characters)

特殊符號的Escape (Remove Meaning of Special Characters)

Using Quote

(WhenQuote When to use quote?)

Command Substitution

Examples of Using Read

Examples of Using Read

Nested if Structure

More Codes

Command Concatenation

Logical NOT AND and OR

File Testing Statement

String Comparison

Numerical Comparison

While and Until

用指令 Shift 設計彈性的引數數目

選項的處理 (Option Processing)

選項的處理 -getopts

群組指令 (Command Group)

怠工指令 (Null Command)

程式的除錯工具:set

指令 eval

複雜的I/O轉向

轉向 及 Here

STDIN 轉向 及 Here

Open a file for both input and output

Signal and Trap

nohup 程序的免死金牌與強迫中止


Untitled Document
Unix 之優缺點
Tue Nov 25 23:14:32 CST 2025 Untitled Document
學習 Unix Shell Programing 之方法
Tue Nov 25 23:14:32 CST 2025 Untitled Document
Design Objective and Procedure
Objective: Minimize Total Time
 
If One Time Execution, Non-Repeatable - Make Coding convenient
If Data is not required - count coding time
If Data is required - count coding time AND data preparation time
 
If Repeatable Procedure - Make User Convenient
data preparation
easy to invoke
easy to memorize
easy to maintian
 
例:
put contact list in a "%name ... " markup format
Convert data into CSV file and text file for online query
download CSV file to PC and sync to PDA
 
例:
put trip list in a "%date ... " markup format
Convert data into HTML file and put into web server
Tue Nov 25 23:14:32 CST 2025 Untitled Document
Shell Language Constructs
Shell 語言之主要元素
 
變數 (Variables) 及相關動作
   
  • 變數型態都是字串
  • 變數代換 (Variable substitution)
  • 指令代換 (Command substitution)
  • 檔名之產生 (File Name Generation)
 
用於流程控制的語言元素 (Control Flow Primitives)
   
迴圈式執行 (Repetitive Execution)
for
while
until
   
無條件程式跳躍 (Unconditional transfer of control)
break
continue
exit
   
條件式執行 (Conditional Execution) |
if-then-else
case
 
函數 (Function)
   
  • 與一般傳統函數大同小異
  • 使用位置變數 (Position Variables: $1, $2, ... $9, $*) 傳遞函數之參數
Tue Nov 25 23:14:33 CST 2025 Untitled Document
指令之執行步驟 (Command Line Evaluation)
   
一支電腦程式即使語法及邏輯都正確,也可能因為系統問題而發生錯誤, 例如 Memory Leak 這種問題。 而Shell Programming 也是一樣, 一支 shell script 即使語法及邏輯都正確, 也可能因為各種跟語法無關的問題而發生錯誤。 幾乎所有初學者都被"File Not Found"這個常見的錯誤訊息困擾過。 很不幸的,Shell programming 的系統環境更為複雜, 千奇百怪的疑難雜症更多,除錯之路更為曲折, 很多初學者都卡關在這種問題上。 我們必須熟悉Shell 如何執行一個指令, 才能順利下一個指令或設計一個穩定高度可靠的 script。
 
問題一: 當使用者下一個含有許多特殊符號的指令時,Shell 是如何執行它的?
Script cmdexe-1
mycommand -l $option `echo $filelist` file*.??? 
 
問題二: 下面的script 語法正確,但卻不安全,是甚麼原因?如何改進?
Script cmdexe-2
 [  $1  -eq 3 ] && echo 3  || echo not 3 
 
Shell 執行指令之步驟
   
Shell 在真正執行指令之前,有許多前置工作必須先做, 否則指令便無法執行, 例如:所要的執行檔在哪裡?變數裡的內容又是如何? 如果所需檔案的名稱含有萬用字元,也需先展開取得全部的所需檔案。 所以一個 Shell Programmer 必須知道 Shell 是如何執行一個 指令的,可以避免很多執行面的錯誤,也能較為快速的除錯。 Shell 執行指令的步驟如下:
讀進一整個指令(一行或多行)
進行變數代換,展開之
進行指令代換,展開之
處理 I/O 轉向
將參數依據間隔符號斷開,得到參數序列
如有檔案之萬用字元,展開之
如含有'=', 執行之,將'=' 左邊之變數賦值
根據環境變數 PATH之記錄,循所指定之路徑尋找$0(使用者所下的指令) 所指定之執行檔
將所有參數交給執行檔並執行之
   
根據以上步驟,我們來檢視 cmdexe-2 的問題。 假設變數 $1 裡面是 10, 執行如下,結果沒有問題;
 [ 10 -eq 3 ] && echo 3  || echo not 3
但如果變數 $1 裡面是空的(尚未定義,或定義成空字串), 問題就出來了,執行過程如下;
 [   -eq 3 ] && echo 3  || echo not 3 
在執行cmdexe2 中的 test (就是[ ]) 指令時,Shell 交給 test 兩個參數, "-eq" 及 "3", 結果參數的數量不對,因為指令 test 在執行 "-eq" 時必需取得左右兩個 數值,才能運作。
   
將 script cmdexe-2 改成 cmdexe-3
Script cmdexe-3
 [ "$1" -eq 3 ] && echo 3  || echo not 3 
執行過程如下,問題就解決了
 [ "" -eq 3 ] && echo "3"  || echo "not 3" 
Tue Nov 25 23:14:33 CST 2025 Untitled Document
未定義變數 (Undefined Variable)
變數之定義
   
一個變數在初次被賦值(Assignment),就自動被定義了,不需事先宣告。
   
一個變數在未被定義前就被使用,其值為"空字串"
   
一個變數可以被賦值為空字串如下:
 
a=
b=""
Tue Nov 25 23:14:33 CST 2025 Untitled Document
Variables Type
變數型態 (Variable Type)
   
所有變數的型態都是「字串」,包含下列字元:
  • 大小寫英文字母 (upper and lower case letters)
  • 數字 (digits)
  • newline 符號
  • 空白
  • tab
  • 一些特殊符號
Tue Nov 25 23:14:33 CST 2025 Untitled Document
Variable Naming Convention
First character must be letter or underscore
Arbitrary sequence of letters, underscores, and digits
length depends on system implementation

Acceptable Names Unacceptable Names
Myfile2
_pay2me_
acct!
2ndfile
$owed
Tue Nov 25 23:14:33 CST 2025 Untitled Document
Variable Assignment
變數之賦值
   
用"="可為變數賦值,例如:
file=maillist
value=4+5
name=
FullName='John Smith'
formula='a<b'
name='Robert(Bob)'
list='read;practice;test'
list="read;practice;test"
注意要用用 Quote ("" 或 '') 將特殊符號保護起來。

   
變數可賦值為空字串:
 a= 
一個變數在未賦值前,內含空字串
Tue Nov 25 23:14:34 CST 2025 Untitled Document
變數之展開 (Variable Substitution)
   
一個變數在定義時,變數名稱前面不須加'$'符號,但 在展開一個變數時,變數名稱前面須加上'$'符號:
name=Bob
echo $name
echo ${name}
echo $HOME
echo ${HOME}
必要時,變數名稱前後可加上大括弧'{}' 以避免產生混淆,例如:
執行結果
x=big
xfoot=small
a=$xfoot
b=${x}foot
c=${xfoot}
echo $a
echo $b
echo $c
small
bigfoot
small
Tue Nov 25 23:14:34 CST 2025 Untitled Document
設定變數的預設值 (Default Values in Variable Expansion)
   
當一個尚未定義的變數被使用時,若無其他設定,其值將將預設為空字串, 有時候會對shell script 的執行產生阻礙。 POSIX 標準讓程式設計師可以事先設定遇到此種情況時的應對方式, 可以防止這種情形發生。兩種格式如下:
${var[-=?+]default}

${var:[-=?+]default}
冒號(:)在設定預設值時的意義如下:
有冒號 未定義或含有空字串的變數都會觸發設定的應對
沒有冒號 僅有未定義的變數會觸發設定的應對
符號 應對方式 例 (Colon) 例 (No Colon)
- 變數var如未定義,則回傳預設值default
  變數 var 維持不變
echo ${var:-default} echo ${var-default}
+ 變數var如有定義,則傳回 default 值,
  var 之內容維持不變
echo ${var:+default} echo ${var+default}
= 變數var如未定義,則回傳預設值default
  並將變數 var 賦值為 default
echo ${var:=default} echo ${var=default}
? 變數var如未定義,則印出Message,
  而 Shell 程序則終止執行
echo ${var:?message} echo ${var?message}
範例
%echo ${var}
 
%echo ${var:-Null}
Null
%echo ${var:+notNull}
 
%echo ${var:=toset}
toset
%echo ${var}
toset
%var=LoveYou
%echo ${var}
LoveYou
%echo ${var:-Null}
LoveYou
%echo ${var:+notNull}
notNull
%echo ${var:=toset}
LoveYou
Tue Nov 25 23:14:34 CST 2025 Untitled Document
變數展開的POSIX擴充功能-字串與算數運算
   
原始的 Bourne Shell 並沒有內建數值運算與字串的處理功能, 只能依靠外部指令來完成數值運算與字串處理,非常不方便,效率也差, 有鑑於此,POSIX 特地在其標準中將變數展開這個功能加以擴充, 加入有限的數值運算與字串處理能力。
語法 功能
${var:offset:length} 擷取字串中之片段 (extract sub-string)
${var/pattern/string} 代換字串中之片段(substition)
${#var} 計算字串之長度
${var#pattern} 刪除字串之單一前綴(delete single prefix)
${var##pattern} 刪除字串之最長前綴(delete longest prefix)
${var%pattern} 刪除字串之單一後綴(delete single postfix)
${var%%pattern} 刪除字串之最長後綴(delete longest postfix)
${${!prefix*}
${!prefix@}
列出變數名稱
$((expression)) 算數運算
Tue Nov 25 23:14:34 CST 2025 Untitled Document
變數展開的字串處理功能
字串串接
   
在 Shell 中,將兩個字串黏在一起(concatenation)是 很簡單的,例如:
 $var1$var2     
#------------------------------------------- 
#將 $var1 及 $var2 展開後,黏在一起。
但是其他的字串處理,就很無能為力了,必須依靠 sed 等指令來處理字串,例如:
a=`echo $var | sed 's/nccu/NCCU/'`      
#----------------------------------------------------------------
#將變數$var的內容展開交給sed將裡面的字串nccu改成NCCU,然後賦值於變數$a 

擷取字串中之片段   ${var:offset:length}
 
方法一: 用指令 'cut'
範例
%s=0123456789
%echo $s | cut -c3-6 #擷取第3-6字元
2345
 
方法二: 用POSIX 變數展開之擴充功能 ${var:offset:length}
   
先展開變數 $var,將展開的字串從offset 開始截取長度為length 的片段,頭尾去除。 length 若從缺,則截到到字串的尾端。
範例
%s=0123456789
%echo ${s:2:4}
2345
%echo ${s:2:10}
23456789
%echo ${s:2}
23456789
代換字串中之片段   ${var/pattern/string}
 
方法一: 用 'sed'
範例
%s=0123456789
%echo $s | sed 's/012/abc/'
abc3456789
 
方法二: 用POSIX 變數展開之擴充功能 ${var/pattern/string}
   
先展開變數 $var,將展開的字串跟'pattern'比對, 若有符合,則用'string'取代所有符合的部分。 #--------------------------------------------
#註: 檔案名稱之萬用字元在'pattern' 中是起作用的。
範例
%s=0123456789
%echo ${s/012/abc}
abc3456789
%echo ${s/0?2/abc}
abc3456789
%echo ${s/0*4/abc}
abc56789
   
'pattern' 的開頭如果有下列符號,則有特殊意義:
/ 所有符合 'pattern'的字串都被取代 ${var//pattern/string}
# 'pattern' 只有在字串之開頭才符合 ${var/#pattern/string}
% 'pattern' 只有在字串之後頭才符合 ${var/%pattern/string}
範例
%s=abcabcABC
%echo ${s//abc/123}  #echo $s| sed 's/abc/123/g'
123123ABC
%echo ${s/#abc/123}  #echo $s| sed 's/^abc/123/'
123abcABC
%echo ${s/%abc/123}  #echo $s| sed 's/abc$/123/'
abcabcABC
%echo ${s/#ABC/123}  #echo $s| sed 's/^abc/123/'
abcabcABC
%echo ${s/%ABC/123}  #echo $s| sed 's/abc$/123/'
abcabc123
計算字串之長度   ${#var}
   
範例
%x=supercalifragilisticexpialidocious
%echo There are ${#x} characters in $x
There are 34 characters in supercalifragilisticexpialidocious
刪除字串之單一前綴   ${var#pattern}
   
先展開變數 $var,將展開的字串從前面跟'pattern'比對, 若有符合,則刪除第一個符合的部分, (如果 $var 不是以 'pattern' 開頭則不會進行任何動作)
範例
%var=abcde
%echo ${var#ab}
cde
%echo ${var#bc}
abcde
%path=/a/b/long.file.name
%echo ${path#/*/}
b/long.file.name
刪除字串之最長前綴   ${var##pattern}
   
先展開變數 $var ,將展開的字串從前面跟'pattern'比對, 若有符合,則刪除所有符合的部分, (如果 $var 不是以 'pattern' 開頭則不會進行任何動作) 此功能可以用來取代 basename 中去除path name 的功能 也可以用來截取副檔名
範例
%var=ababcde
%echo ${var#ab}
abcde
%echo ${var##ab}
cde
%echo ${var##bc}
ababcde
%path=/a/b/long.file.name
%echo ${path#/*/}
b/long.file.name
%echo ${path##/*/}
long.file.name
刪除字串之單一後綴   ${var%pattern}
   
先展開變數 $var ,將展開的字串從後面跟'pattern'比對, 若有符合,則刪除第一個符合的部分, (如果 $var 不是 'pattern' 在字串之最後不會進行任何動作)。 此功能可以用來取代 basename。
範例
%var=abcde
%echo ${var%de}
abc
%echo ${var%bc}
abcde
%path=/a/b/long.file.txt
%echo ${path%.*}
/a/b/long.file
刪除字串之最長後綴   ${var%%pattern}
   
展開變數 $var ,將展開的字串從後面跟'pattern'比對, 若有符合,則刪除所有符合的部分, (如果 $var 不是 'pattern' 在字串之最後不會進行任何動作)。
範例
%var=abcdede
%echo ${var%de}
abcde
%echo ${var%%de}
abc
%path=/a/b/long.file.txt
%echo ${path%.*}
/a/b/long.file
%echo ${path%%.*}
/a/b/long
列出變數名稱   ${!prefix*}, ${!prefix@}
   
將所有變數中以'prefix'開頭的變數名稱列出來,
'*'所有列出的變數是一個字串,
'@'每一個列出的變數是一個字串。
範例
%a1=1
%a2=2
%a3=3
%echo ${!a*}
a1 a2 a3
Tue Nov 25 23:14:35 CST 2025 Untitled Document
檔案名稱的字串處理
   
Shell programming 常常需要處理檔案名稱, 例如: cc 這個編譯器在編譯一個 C 檔案,如 example.c時,會產生中間檔案'example.o',所以編譯器的執行過程中, 須將'example.c'去掉副檔名將字根'example'從檔案名稱中抽取出來, 再貼上'.o'字串,最終產生一個'example.o'的字串作為中間檔名。 檔案的轉換,例如 'file.txt' 轉成 CSV 檔時,要產生一個新黨名 'file.csv' 也需要將字串 'file' 自 'file.txt' 中抽取出來。 有時候需要將路徑抽取出來,或截斷。我們以 '/a/b/file.txt'為例, 歸納幾個常見的檔名處理需求:
需求說明 結果
擷取副檔名 txt
裁掉副檔名 /a/b/file
擷取目錄路徑 /a/b
裁掉目錄路徑 file.txt
裁掉副檔名及目錄路徑 file
而很多Unix版本都會提供一個很有用的指令 'basename' 可用來將檔案名稱去掉副檔名。 與 'basename' 類似的,'dirname' 可用來將檔案名稱中的檔名去除而留下目錄的路徑名稱。 配合 sed 的字串編輯功能,就能輕易滿足上述需求。如果系統 具備POSIX的擴充功能,則更為簡單。
 
擷取副檔名
Script getext_v1
s=/a/b/file.txt
echo $s | sed 's/..*\.//'
Script getext_v2
s=/a/b/file.txt
echo ${s##*.}
 
裁掉副檔名
Script cutext_v1
#assume extension name is known
#---------------------------
s=/a/b/file.txt
echo $s | sed 's/.txt$//'
Script cutext_v2
s=/a/b/file.txt
echo ${s%.*}
 
擷取目錄(資料夾)路徑
Script getpath_v1
s=/a/b/file.txt
echo `dirname $s`
Script getpath_v2
s=/a/b/file.txt
echo ${s%/*}
 
裁掉目錄路徑
Script cutpath_v1
s=/a/b/file.txt
basename $s
Script cutpath_v2
s=/a/b/file.txt
echo ${s%%/*/}
 
裁掉副檔名及目錄路徑
Script getroot_v1
#assume extension name is known
#---------------------------
s=/a/b/file.txt
basename $s .txt  
Script cutroot_v2
s=/a/b/file.txt
t=${s%%/*/}
echo ${t%/.*}
Tue Nov 25 23:14:35 CST 2025 Untitled Document
變數展開的算數計算功能 (Arithmetic Expansion)
   
數學運算式的符號如下:
$((expression))
運算式(expression)的語法與C 語言的運算式相同。 其中,對於特殊符號而言,雙括弧有如double quote ("" '") 一般而言, 除了 「"」之外的特殊符號不須另外再用"\" 來escape。
運算子 意義 結合律方向
++ --
Increment and decrement,
prefix and postfix
L2R
+ - ! ~
Unary plus and minus;
logical and bitwise negation
R2L
* / %
Multiplication, division,
and remainder
L2R
+ -
Addition and subtraction
L2R
<< >>
Bit-shift left and right
L2R
< <= > >=
Comparisons
L2R
== !=
Equal and not equal
L2R
&
Bitwise AND
L2R
^
Bitwise Exclusive OR
L2R
|
Bitwise OR
L2R
&&
Logical AND
L2R
||
Logical OR
L2R
?:
Conditional expression
R2L
= += -= *=
/= %= &= ^=
<<= >>= |=
Assignment operators
R2L
   
括弧的作用與 C 語言中的括弧是一樣的。 例如:
運算式 結果
$((3 > 2)) 1
$(( (3 > 2) | | (4 <= 1))) 1
   
在邏輯運算中,非零的數值等於「真」,例如:
$ echo $((3 && 4))      #Both 3 and 4 are "true" 
#-----------------------------------------
結果:  1 
   
例:
 $i=5
 $j=3
 $echo $((i+j)) 
  8
 $echo $((i++)) $i
  5 6
 $echo $((++i)) $i
  7 7
Tue Nov 25 23:14:35 CST 2025 Untitled Document
Arithmetic Statement in KSH
 
integer i=0
 
Use (( C-like expression )) in testing
 
Let C-like expression (no space or Tab is allowed)
 
'Let' can be omitted if variables are declared
Tue Nov 25 23:14:35 CST 2025 Untitled Document
String Concatenation
字串的連接(Concatenation)
   
在 Shell 中,字串的連接(Concatenation)是非常簡單的, 將所有字串直接連在一起就行了。
結果
 
a=first
b=second
echo $a$b
echo ${a}and${b}
echo "$a and $b"
 
firstsecond
firstandsecond
first and second
Tue Nov 25 23:14:36 CST 2025 Untitled Document
Environment
變數的有效範圍及環境變數之設定
 
變數的有效範圍 (Scope of Environment variable)
每一個 shell 程序擁有自己的變數
當一個變數在一個 程序中被定義之後,就一直存在直到程序結束
一個變數除非被宣告為環境變數(利用export指令), 否則不會遺傳給子程序,因此,父程序與子程序之間彼此不知道各自所定義 的變數。
子程序所定義的環境變數,父程序無法看到。
子程序可以使用父程序所定義的環境變數並更改之,但所做的改變 無法影響到父程序,亦即,父程序在呼叫子程序後其環境變數維持不變,不受 子程序的影響。
 
環境變數之設定
   
許多應用程式依賴某些環境變數,才能順利運作,例如 vi 就必須依靠 $TERM 所告知的終端機型號,才能從 /etc/termcap 或 /etc/terminfo 中取得所需的游標控制參數,否則 vi 便無法控制游標了。 使用者必須將 $TERM 事先定義於 .profile 中,讓login shell 在 login 程序中自動執行 '.profile'來自動設定 $TERM, 才能與使用者進行雙向溝通。否則使用者可能連login: 的符號都看不到了。
   
除了 $TERM 之外,還有很多環境變數(例如 $PATH) 都必須事先定義於 '.profile' 中。 一個系統管理者在新增加一個使用者時,必須做的相關 設定中一定要做的就是放一個標準版本的 '.profile' 在使用者的 $HOME 目錄,並在其中設定 $HOME, $TERM, $PATH 等環境變數,否則使用者一進系統,連'ls','cat' 這些指令都不能用,因為 login shell 根本不知道如何尋找這些外部指令。
   
如果使用者不慎破壞了'.profile' 這個檔案,那下次 login 時, 會遭遇到很大的麻煩, 最壞的情形是要請系統管理者再複製一份標準的 '.profile' 到 $HOME 檔案。
   
話說回來,一個常寫 shell script 的人不可能不去改變環境變數, 例如,使用者將所寫的 shell script 集中到 $HOME/bin 去,然後 將$HOME/bin 加到 $PATH 變數去,以便在任意的目錄下使用這些 script。 一個比較安全的作法是:盡可能將所要執行的設定放在 另一個 shell script 中,例 '.myprofile',然後在 原始 '.profile' 的最後一行加上這麼一行 code:
  
  • .myprofile
  • 上面這行code 會在login shell 執行(請注意最前面的'.',其意為在current shell 執行), 執行完畢後,環境變數的內容 會一直保持著直到使用者log out。下面這兩種方法都是無效的,因為 它們都是利用子程序去執行的,而子程序是無法變更父程序裡的環境。
    # ---------------------------
    # 無效的環境變更方式
    # --------------------------- 
    sh .myprofile # ---------------------------
    .myprofile
    Tue Nov 25 23:14:36 CST 2025 Untitled Document
    特殊變數
    利用特殊變數 $$ 產生唯一的檔案名稱
       
    Unix 是一個多人多工的系統,難免會在檔案的產生或更動時產生衝突,導致資料的喪失或錯誤。最常見的是執行過程中產生的暫存檔,例如以下的 swap script
    # -----------------
    # Script swap v1 
    # ----------------- 
    mv $1 tmp.o mv $2 $1 mv tmp.o $2
    如果有兩個 session 在同一個目錄下同時執行這個 script , 那 tmp.o 這個中間暫存檔就會因為名稱衝突而造成大問題,導致檔案的遺失。有時候暫存檔會放在某些特殊目錄,例如: /tmp, 那問題更大,因為不管任何人在哪一個目錄執行,都會產生問題:
    # -----------------
    # Script swap v2 
    # ----------------- 
    mv $1 /tmp/tmp.o mv $2 $1 mv /tmp/tmp.o $2
    為了避免這種問題,在命名時要為每次執行時產生的中間檔案取一個不同 的名字。取亂數是一個方法,將檔案名稱加上時間戳記也是另一個方法, 還有一個更簡單的辦法是加上程序的序號。一個程序的序號可從 $$ 這個特殊變數取得。如此, script swap 就可改寫成 script v3 這個比較安全的 script:
    # -----------------
    # Script swap v3 
    # ----------------- 
    mv $1 /tmp/tmp$$.o mv $2 $1 mv /tmp/tmp$$.o $2
    Tue Nov 25 23:14:36 CST 2025 Untitled Document
    Special Variables for Position Parameters $* and $@
       
    使用者在使用一個指令時,如果輸入引數(Position Parameter)的資料含有空白,那位置變數(Position Variable) 可能會取到錯誤的資訊,使用者在輸入引數資訊時,必須用引號(" " 或 ' ') 保護起來,以免錯亂。而在 script 中使用 $*, $@時也須用 引號保護,否則含有空白的參數會被斷開。 這兩個代表全部引數的位置變數有些微不同, 請看下面的說明瞭解其區別。
    位置變數 意義
    $*
    $1 $2 $3 $4 ...
    $@
    $1 $2 $3 $4 ...
    "$*"
    "$1 $2 $3 $4 ... "
    "$@"
    "$1" "$2" "$3" "$4" ...
     
    Script 執行及結果
    # -------------------------------------
    # Script: myname
    # ------------------------------------- 
    echo "First Name: $1" echo "Last Name: $2"
    # -------------------------------------
    # Script: getname
    # ------------------------------------- 
    echo You have entered: $* echo '=== Passing $* ==='; myname $* echo '=== Passing $@ ==='; myname $@ echo '=== Passing "$*" ==='; myname "$*" echo '=== Passing "$@" ==='; myname "$@"
    %getname "Yao Nan" Lien
    You have entered: Yao Nan Lien
    === Passing $* ===
    First Name: Yao
    Last  Name: Nan
    === Passing $@ ===
    First Name: Yao
    Last  Name: Nan
    === Passing "$*" ===
    First Name: Yao Nan Lien
    Last  Name: 
    === Passing "$@" ===
    First Name: Yao Nan
    Last  Name: Lien
    
    Tue Nov 25 23:14:36 CST 2025 Untitled Document
    特殊符號 (Special Characters)
       
    除了文字型態的關鍵詞之外,Shell 還定義了一些特殊符號,

    SP
    Tab
    NL
    =
    ;
    &
    )
    (
    >
    <
    |
    ^
    {
    }
    $
    ;
    #
    *
    ?
    [
    ]
    `
    \
    '
    "
    Tue Nov 25 23:14:37 CST 2025 Untitled Document
    特殊符號的Escape (Remove Meaning of Special Characters)
    在字串中使用特殊符號
       
    字串中如果含有特殊符號,必須保護(escape)起來,否則Shell 會誤解而啟動相應的特殊動作。共有三種保護機制:
     \ 
       ' '  
       "  "  
     
    1. Backslash ('\')
    .
    Backslash '\' 保護單一的符號
    # ---------------------
    # example  single'\' 
    # ---------------------- 
    full=Joe\ Smith
    在一行的末尾接一個'\'
    可將次行連接起來,
    合併成同一行。
    # ---------------------
    # example '\' before NL
    # ---------------------- 
    echo this is a \ test
    兩個連續的 Backslash '\\'
    # ---------------------
    # example '\\'
    # ---------------------- 
    echo \\ is one backslash
     
    2. 單引號(' ')
       
    所有特殊符號均失去特殊意義。
     
    3. 雙引號(" ")
       
    除了下列四個符號外,其他的特殊符號均失去特殊意義。
    $
    `
    "
    \
    範例
    %x=this is a string
    is: not found [No such file or directory]
    %x="this is a string"
    %echo $x
    this is a string
    %echo "$x"
    this is a string
    %echo '$x'
    $x
    %echo "*"
    *
    %echo '*'
    *
    雙引號(" ")之使用
       
    當字串中間含有須展開之變數或指令代換時,用雙引號比 單引號更為方便,例如下列兩行 echo 程式碼效果相同:
    # -------------------------------------
    # single quote vs double quote
    # ------------------------------------- 
    name=Clinton echo 'My name is '$name' and today is '`date` # -------------------------------------
    echo "My name is $name and today is `date`"
    Tue Nov 25 23:14:37 CST 2025 Untitled Document
    Using Quote
     
    更多 Script 實例
     
    Part1=One
    Part3=Three 
    echo '$Part1'$Part2"$Part3"
    #----------------------------
    結果: $Part1Three 
    
    Part1=One
    Part3=Three
    echo '$Part1"$Part2"$Part3'
    #----------------------------
    結果: $Part1"$Part2"$Part3 
    
    Part1=One
    Part3=Three
    echo "$Part1'$Part2'$Part3"
    #----------------------------
    結果: One''Three 
    
    # -------------------------------------
    # Script: replaceword v1
    # Usage: replaceword <filename>
    # -------------------------------------  
    OLD_WORD=abc NEW_WORD=xyz sed -e "s/$OLD_WORD/$NEW_WORD/g" < $1
    # -------------------------------------
    # Script: replaceword v2
    # Usage: replaceword <filename>
    # -------------------------------------  
    OLD_WORD=abc NEW_WORD=xyz cat $1 | sed -e "s/$OLD_WORD/$NEW_WORD/g"
    你能看得懂天書嗎?
    date=`date`
    birthday="Tue Apr 28"
    echo $date | awk '/^'"$birthday"'/ {print $2, $3;}'
    
    Tue Nov 25 23:14:37 CST 2025 Untitled Document
    (WhenQuote When to use quote?)
       
    此外,當一個字串裡面含有變數時,大大提高了不確定性,程式設計師 並不一定知道變數將來會充什麼樣的值進來,可能造成危險,下面 的 shell script 就非常危險:
    rm -rf /$var    #如果 $var 是空字串,會有什麼後果?
    
     
    'test' 指令的陷阱
    Script 安全性
    test "Clinton" = "Clinton" 安全
    test Clinton = Clinton 安全
    test "Bill Clinton" = "Bill Clinton" 安全
    test Bill Clinton = "Bill Clinton"
    test "" = "Clinton" 安全
    test   = "Clinton"

    安全
    string=""
    test $string = Clinton
    
    string=""
    test '$string' = Clinton
    
    string=""
    test "$string" = Clinton
    

    安全
    string="Bill Clinton"
    test $string = Clinton
    
    string="Bill Clinton"
    test '$string' = Clinton
    
    string="Bill Clinton"
    test "$string" = Clinton
    
    Tue Nov 25 23:14:37 CST 2025 Untitled Document
    Command Substitution
    Command Substitution
       
    在C 語言內,執行一個函數 (Function)時,Function 所傳回的值會被程式抓住直接使用。 而在 shell script 中,各外部指令可能會將執行結果輸出到 STDOUT,如果要將指令的執行結果抓回script內運用時,要如何做?
       
    Shell 內提供數種方法:
    例: command-substitution v1 執行結果
    str="Current directory is    `pwd` "
    echo $str
    

    str="Current directory is    $(pwd) "
    echo $str
    
    Current directory is /usr/lien
    
    註: pwd 這個命令輸出"/usr/lien",
    而後整個字串代替原來的`pwd`,
    填入字串,用來設定str 變數。

     
    更多例子:
    today=`date`
    echo "Today is $today"
    
    echo "Today is `date`"
    
    echo "Today is $(date)"
    
    number=`expr $number + 1`
    
    number=$((number++))
    
    #----------------------
    # 印出目錄下所有檔案
    #---------------------- 
    for i in  `ls` 
    do
    echo "==== File Name: $i ===="
    cat $i
    done
    
    Tue Nov 25 23:14:37 CST 2025 Untitled Document
    Examples of Using Read
     
    利用 'read' 從 /etc/passwd 檔案中抽取資訊
    while IFS=: read user password uid realname homedir shell
    do
       echo user $user is $realname who prefers $shell 
    done < /etc/passwd
    
     
    cat /etc/passwd | 
    while IFS=: read user password uid realname homedir shell
    do
       echo user $user is $realname who prefers $shell 
    done 
    
    Tue Nov 25 23:14:38 CST 2025 Untitled Document
    Examples of Using Read
    複製整個目錄架構,但不複製其中的檔案
     
    1. DOS 中利用 xcopy /t 指令
     
    2. 利用下面兩個 shell script
    Script cpdir_v1
    find /a/b -type d       |\ #find all directories under /a/b 
    sed 's;/a/b/;/home/b/;' |\ #change to destination path name 
    sed 's/^/mkdir /'       |\ #insert mkdir command 
    sh -x                      #nvoke sh to execute 
    
    Script cpdir_v2
    find /a/b -type d       |\ #find all directories under /a/b 
    sed 's;/a/b/;/home/b/;' |\ #change to destination path name 
    while read newdir          #read new directory name 
    do
       mkdir $newdir           #make new directory 
    done
    
    Tue Nov 25 23:14:38 CST 2025 Untitled Document
    Nested if Structure
       if 
          write stu1 < message
       then
          :
       else 
         if mail stu1 < message
         then 
    	echo "mail sent instead"
         else
    	echo "Could not sent instead"
         fi
       fi
    
    Tue Nov 25 23:14:38 CST 2025 Untitled Document
    More Codes
    for person in stu1 stu2 stu3
    do
       if 
          who | grep $person > /dev/null
       then
          write $person < message
       else 
          mail $person < message
       fi
    done
    
    Tue Nov 25 23:14:38 CST 2025 Untitled Document
    Command Concatenation
       
    兩個指令可以用 && || 有條件的連接起來
     

     command1 && command2    
    =
     
    if command1
    then
       command2
    fi
    

    例: mkdir newdir && cp thisfile newdir
    
     

     nbsp; command1 || command2   
    
    =
    if ! command1
    then
       command2
    fi
    

    例: 
    rm myfile || echo "ERROR: cannot remove myfile"
    Tue Nov 25 23:14:38 CST 2025 Untitled Document
    Logical NOT AND and OR
       
    && || 也可以用在 測試條件式中連同作為 AND 及 OR ,連同 NOT 作為 邏輯運算的運算子 (logical operator)。
     
    NOT
    if ! grep QQ fileX > dev/null
    then
      echo QQ not found in fileX
    fi 
    
    =
    if grep QQ fileX > /dev/null
    then
    :    # do nothing 
    else
      echo QQ not found in fileX
    fi
    
     
    AND
    if grep pattern1 myfile && grep pattern2 myfile
    then
       echo myfile contains both patterns
    fi 
    
     
    OR
    if grep pattern1 myfile || grep pattern2 myfile
    then
       echo Neither pattern1 nor  pattern2 found in myfile 
    fi
    
    Tue Nov 25 23:14:39 CST 2025 Untitled Document
    File Testing Statement
    測試檔案性質 (Test File Status)

    test -r filename
    Exists and readable
    test -w filename
    Exists and writable
    test -x filename
    Exists and executable
    test -f filename
    Exists and regular
    test -d filename
    Exists and directory
    test -s filename
    Exists and nonzero size
       
    我們用各種格式來撰寫一個複合的條件式程式碼,讀者可以 觀摩各種增加程式可讀性的技巧。
    for file 
    do 
       if test -f $file
       then
          if  test -r $file
          then 
    	 if test -x $file
    	 then
    	    echo "File $file is 
                readable and executable"
    	 fi
          fi
       fi
    done
    
    for file 
    do 
       if [ -f $file ]
       then
          if  [ -r $file ]
          then 
    	 if [ -x $file ]
    	 then
    	    echo "File $file is 
                readable and executable"
    	 fi
          fi
       fi
    done
    
    for file 
    do 
       if [ -f $file -a -r $file  -a -x $file ]
       then 
           echo File $file is readable and executable
       fi
    done
    
     
    for file 
    do 
    [ -f $file -a -r $file  -a -x $file ] && echo File $file is readable and executable
    done
    
    Tue Nov 25 23:14:39 CST 2025 Untitled Document
    String Comparison
    test "s1" = "s2"
    Strings equal
    test "s1" != "s2"
    Strings not equal
    test -z "s1"
    Length of string zero
    test -n "s1"
    Length of string nonzero
    test "s1"
    Length of string nonzero
       
    • "test" 所需的參數之間以空白隔開。
    • 注意用雙引號(" ")保護參數,防止空字串破壞程式結構。
    例 Script 1 例 Script 2
    noset=""
    test -z  $noset
    test  "$noset" = a 
     test  $noset = a  
    q=`who -T | grep "^s8500" | cut -c10`
    if test "$q" = "+"
    then 
       write s8500 < note
    else
       mail s8500 < note
    fi
    
    Tue Nov 25 23:14:39 CST 2025 Untitled Document
    Numerical Comparison
    test "$n1" -eq "$n2"
    Equal
    test "$n1" -ne "$n2"
    Not equal
    test "$n1" -gt "$n2"
    Greater than
    test "$n1" -ge "$n2"
    Greater than or equal to
    test "$n1" -lt "$n2"
    Less than
    test "$n1" -le "$n2"
    Less than or equal to
     
    # -----------------------------------------------
    # countuser: count number of users on the system
    # ----------------------------------------------- 
    count=`who | wc -i` if [ "$count" -le "20" ] then echo "$count" users - light load else echo "$count" users - heavy load fi
    Tue Nov 25 23:14:39 CST 2025 Untitled Document
    While and Until
     
    Syntax
    while 
       command_lines
    do
       command_lines
    done
    
    until 
       command_lines
    do
       command_lines
    done
    

    'while' will test the condition to do the first iteration
    'until' will do the first iteration before test the condition
    Begin while marks loop start
    Control Evaluate return code from last command before do
    Middle execute all commands within body
    End done marks loop end, shell reads until end is found

     
    例 Script: Appends stdin to file outfile
    echo "
    Enter lines of input (end with ^D):
    "
    while read x
    do
       echo $x
    done >> outfile
    
    Tue Nov 25 23:14:40 CST 2025 Untitled Document
    用指令 Shift 設計彈性的引數數目
    彈性的引數數目
       
    在設計一個script時,如果希望讓使用者給的引數的數目並非固定, 希望有彈性,可用下面的方式做到:
    使用情境
    將數個檔案串接在一起,並標記檔案名稱
    用法
    concatefile <file1> <file2> <file3> ...
    
    Script
    concatefile
    for i     #若沒有敘明引數,相當於 for i in $* 
    do
    echo "====== FILE NAME: $i  ========"
    cat -n $i     #option -n 將讀入的資料加上行數
    done > outfile
    
    不均勻的引數
       
    當使用者給的引數中有異類時,就必須依靠 shift 來做到。例如
    使用情境
    將數個檔案串接在某個檔案(file2append)之後
    用法
    appendfile   <file2append> <file1> <file2> <file3> ...  
    
    Script
    appendfile
    outfile=$1
     shift 
    for i 
    do
       echo ===== FILE: $i =====
       cat $i
    done >>$outfile
    
    Tue Nov 25 23:14:40 CST 2025 Untitled Document
    選項的處理 (Option Processing)
       
    一個功能強大的指令通常會有許多option 讓使用者選擇,否則便要製造出很多 不同版本的指令,大大的增加維護的成本及使用者的記憶負擔。而 option 可能 非常靈活而有不同的組合,但處理起來便非常麻煩。以下的例子利用 case/if 等方式 硬生生的處理一個複雜的 option 組合。
    file=  
    verbose=  
    quiet=  
    long=
    while [ $# -gt 0 ]   #Loop until no args left 
    do
       case $1 in   #Check first arg 
       -f) file=$2
          shift   #Shift off "-f" so that shift at end gets value in $2 
          ;;
       -v) verbose=true; quiet=  ;;
       -q) quiet=true; verbose=  ;;
       -l) long=true             ;;
       --) shift   #By convention, - - ends options 
           break
           ;;
       -*) echo $0: $1: unrecognized option >&2 ;;
        *) break  ;;    # Nonoption argument, break while loop 
       esac
       shift   #Set up for next iteration 
    done
    
    Tue Nov 25 23:14:40 CST 2025 Untitled Document
    選項的處理 -getopts
       
    為了節省處理 Option 的麻煩,POSIX 將option 的格式統一做了規範 並提供了一個 getopts 的指令。大部分Unix 的使用者對於在 Shell 控制下,在指令後面下 option 的格式,應該都有了一定的瞭解,

    每一個option 都是單一字元
    數個option 可以合在一起變成一個字串
    每一個option 或每一組合在一起的 option 前面都有個 "-" 字元
    一個 option 後面可以帶有參數
    用 "--" 代表option 的結束
    其他引數接在 option 後面

    'getopts' 指令可協助一個 shell script 剖析 option,提供不少方便。 程式設計師可用一個迴圈將 option 逐個讀進來交給 'getopts' 處理。 'getopts'這個指令跟其他指令最大不同點是,它保有「記憶」。其他所有 指令每次執行都是獨立的,除了跟動態資訊(例如時間)有關的指令之外, 只要執行條件相同,得到的結果都必定相同,不會跟執行順序有關,但 'getopts' 在一個迴圈中執行時,每一次執行都有不同的結果,所以它 可以記住上一次執行到哪一個 option,這是跟其他的指令大為不同之處。 'getopts' 需要兩個參數:

    第一個參數敘明所有的 option。(所有option 的字元合併成一個字串) option 後面如需要參數,則在該字元後面加一個冒號(':')。例如下例中 的 'f:vql'。
    第二個參數是一個變數,用來記錄每次剖析使用者所給的 option 的結果

    'getopts' 在執行時,為產生兩個變數, $OPTIND 記錄目前剖析到第幾個 option,而$OPTARG 則記錄option 後面所帶的參數。以下是一個使用 'getopts'的例子:
     
    # -----------------------------------------------
    # getops v1
    # ----------------------------------------------- 
    file= verbose= quiet= long= while getopts f:vql opt do case $opt in #Check option letter f) file=$OPTARG ;; v) verbose=true; quiet= ;; q) quiet=true; verbose= ;; l) long=true ;; esac done shift $((OPTIND - 1)) # Remove options, leave arguments

       
    使用者所給的option 之前的'-' 符號自動被除掉
    如有'--'符號,也自動被除掉
    如果使用者給了一個不存在的符號當option, 'getopts' 或自動產生錯誤訊息。但程式設計師可選擇自行處理,只要 在'getopts' 的第一個參數之最前面加上冒號(':'),getopts 就不會產生錯誤訊息,而照常剖析之。
    # -----------------------------------------------
    # getopts v2 : 忽略錯誤訊息的選項處理
    # ----------------------------------------------- 
    file= verbose= quiet= long= while getopts :f:vql opt do case $opt in Check option letter f) file=$OPTARG v) verbose=true; quiet= ;; q) quiet=true; verbose= ;; l) long=true; ;; '?') echo "$0: invalid option -$OPTARG" >&2 echo "Usage: $0 [-f file] [-vql] [files ...]" >&2 exit 1 ;; esac done shift $((OPTIND - 1)) Remove options, leave arguments

    Man Page of 'getopts'

    Tue Nov 25 23:14:40 CST 2025 Untitled Document
    群組指令 (Command Group)
       
    設計師可以將數個指令組成一個群組,當作一個指令來做,格式如下:
     
      { command-list; }    #在 current process 執行 
     
      ( command-list )    #在 subshell 執行 
       
    群組指令在 I/O 轉向時非常有用,可以將一群指令的輸出用單一個 I/O轉向完成之。
     
    { date; ls } > filelist
    # -------------------------------
    { ...; ... } < inputfile
    # -------------------------------
    command | { ...; ... } 
    
       
    這兩種方式對執行環境的影響各有不同 :
    例 Script 執行結果對環境的影響
    { cd x; rm junk }
    
    執行完畢後,shell 停留在目錄 x
     
    ( cd x; rm junk )
    
    執行完畢後,shell停留在原目錄
    Tue Nov 25 23:14:40 CST 2025 Untitled Document
    怠工指令 (Null Command)
       
    一個怠工指令 (Null),不執行任何動作,傳回 "ture",格式如下:
     
      : [parameters ...]
    
    'parameter' 可以是變數展開或指令展開等,但對主程式無影響, 有興趣的讀者可以研究 看看有何有趣的用途。
     
    範例

    whileloop v1 whileloop v2 reverse_if
     
    while     : do
      commands
      if  exit_condition
      then
         break
      fi
    done
    
     
    while   true do
      commands
      if  exit_condition
      then
         break
      fi
    done
    
     
    if command-list
    then
         :
    else
       commands
    fi
    done
    

    #這個 script 把 IF 指令的測試條件顛倒,
    #有時候為了程式的邏輯比較清楚,
    #程式設計師會這樣寫程式。
    Tue Nov 25 23:14:41 CST 2025 Untitled Document
    程式的除錯工具:set
    set -v 列印進度訊息 (verbose)
    set -x 列印執行軌跡
    set - 關閉 -v, -x
    Tue Nov 25 23:14:41 CST 2025 Untitled Document
    指令 eval
       
    如果要把一個變數裡的內容展開後當成指令來執行,可用指令 eval。
     
    範例
    No eval With eval
     
    y=CGI
    x='$y'
    echo $x
    
     
    y=CGI
    x='$y'
    eval echo $x 
    
    結果: $y 結果: CGI
    Tue Nov 25 23:14:41 CST 2025 Untitled Document
    複雜的I/O轉向
    一些常見的I/O轉向需求
    將錯誤訊息(STDERR)儲存到一個檔案
    command 2> errfile
    
    將 STDOUT 及 STDERR 分別儲存到兩個檔案
    command > stdout 2> stderr
    command > stdout 2>> stderr
    
    將 STDOUT 及 STDERR 存到同一個檔案
    command > stdout 2>&1
    
    STDOUT 儲存到一個檔案,捨棄 STDERR
     
    command 1> stdout 2> /dev/null 
    
    將 STDOUT 及 STDERR 同時送給 '|'
     
    command  2>&1 | nextcommand
    
    利用 /dev/tty 將 STDOUT 分流
       
    在設計一個具有與使用者互動能力的Script 時,經常會 遇見一個難題: 如果使用者要將正常輸出訊息轉向到一個檔案去, 但仍然希望保留與i script 的對話於螢幕上不被轉向,我們 可將對話轉向到 /dev/tty。
    下面的Script 可將 Script 跟使用者的對話及Script 的運算結果都輸出到 STDOUT 上,如果要存到檔案內, 只能一併轉向,無法分開。
    echo 請輸入姓名:
    read name
    echo 請輸入電話號碼:
    read phone
    echo 請輸入地址:
    read address
    echo "$name, $phone, $address"
    
    這樣的 Script 對使用者很不方便,因為如果將輸出轉向到檔案 後,使用者無法跟Script 對話。
       
    如果要將 Script 跟使用者的對話留在螢幕上,不想被運算輸出一併被轉向 到檔案,可將不想被轉向的訊息都送到 /dev/tty 去,就不會被轉向了。 下面是一個例子:
    echo 請輸入姓名:   > /dev/tty 
    read name
    echo 請輸入電話號碼:   > /dev/tty 
    read phone
    echo 請輸入地址:   > /dev/tty 
    read address
    echo "$name, $phone, $address"
    
    如此,兩種不同的輸出都顯示在螢幕上,但只有送到STDOUT 的會被 轉向,而送到 /dev/tty 的訊息仍然會留在螢幕上。 使用者仍然 可以跟 script 繼續對話。
    Tue Nov 25 23:14:41 CST 2025 Untitled Document
    轉向 及 Here
       
    當一個程式需要從鍵盤輸入資訊, 但不想麻煩使用者跟系統作互動性輸入時, 可用I/O轉向法將STDIN 轉向一個檔案,亦即轉從檔案讀進資料。 資料處理的任務經常會用到這個功能,從鍵盤輸入資料的程式,改由 事先準備好的資料檔案餵進程式。例如:
    data-processing-command < inputfile
    
    儲存編輯指令於檔案
       
    除了資料處理之外,有些情境也可使用 STDIN轉向,例如 編輯器。編輯一個文件的動作,原是由使用者鍵入編輯指令, 其實可以將編輯指令存放於一個檔案,然後用 STDIN轉向 餵給編輯器來編輯。例如,如果要將一批HTML 檔案內的某一個 名詞改掉,我們可用此法配合迴圈批次的執行任務。例如 下面的 ex 編輯指令存於 ex.script 內及執行程式。
    #editor script file: ex.script
    
    1,$s/TSMC/tsmc/g w
    #edit a batch of HTML files using loop and editing script
    
    for i in *.htm do ex $i < ex.script done
    注意,可視性編輯器例如'vi'並不適用此法,因為 vi 必須依賴螢幕的游標才能正確的編輯。
    儲存編輯指令及主程式於單一檔案
       
    上面的方法需要用到兩個檔案,有時並不利於管理, 下面的 script 將兩個檔案整合成一個檔案:
    echo '1,$s/TSMC/tsmc/g
    w' > ex.script
    for i in *.htm
    do
       ex $i < ex.script
    done
    
    利用Here儲存編輯指令及主程式於單一檔案
       
    for i in  *.htm
    do
    ex $i<<%
    1,\$s/TSMC/tsmc/g
    w
    %
    done
    
    Tue Nov 25 23:14:42 CST 2025 Untitled Document
    STDIN 轉向 及 Here
    Here 之例
    # -----------------------------------------------
    # Send emails to top 10 disk space usage users 
    # ----------------------------------------------- 
    cd /home #Move to top of home directories du -s * | #Generate raw disk usage sort -nr | #Sort numerically, highest numbers first sed 10q | #Stop after first 10 lines while read amount name do mail -s "disk usage warning" $name << EOF Greetings. You are one of the top 10 consumers of disk space on the system. Your home directory uses $amount disk blocks. Please clean up unneeded files, as soon as possible. Thanks, Your friendly neighborhood system administrator. EOF done
    Tue Nov 25 23:14:42 CST 2025 Untitled Document
    Open a file for both input and output
    program <> file
     
    To open file for both reading and writing.
       
    It is up to program to be aware of this and take advantage of it
       
    not practically useful
       
    An undocumented feature in V7 Bourne shell
       
    Standardized in the 1992 POSIX standard,
       
    on many systems /bin/sh doesn't support it.
    Tue Nov 25 23:14:42 CST 2025 Untitled Document
    Signal and Trap
    Unix 信號 (Signal)
       
    傳統電腦架構的核心是CPU,當一個程序(Process)在佔用 CPU 執行時,不會主動停下,在多工的作業系統下,作業系統可以 送信號 (Signal)給一個程序中斷之。例如 Unix 系統下,使用者 按下'^C'試圖中斷一個script 的執行程序,作業系統就會送一個 信號給該程序中斷之。
    常用的 Unix Signal
    Signal Name 說明
    0
    EXIT
    中止程序
    Exit from the shell.
    1
    HUP
    STDOUT被停止,程序要中止
    A pseudosignal used by the shell, indicating that the standard output has hung up; sending this signal logs you out.
    2
    INT
    鍵盤上的<Del>中止鍵被按下,程序要中止
    Sent by a <Del> keystroke; sends an interrupt to the current program.
    3
    QUIT
    鍵盤上的 '^C'中止鍵被按下,程序要中止,core dump
    Sent by a <Ctrl>\ keystroke; causes the current program to abort, leaving behind a core dump (for use in program debugging).
    9
    KILL
    強迫中止程序,trap 失效
    Cannot be trapped or ignored; forces the receiving program to die.
    11
    SEGV
    記憶體衝突,立刻中止程序,trap 失效
    Indicates that a segmentation violation (a memory fault within the UNIX system) has occurred. This signal cannot be trapped or ignored by a shell; it invariably terminates the receiving process and causes a core dump.
    15
    TERM
    中止程序
    Terminates the receiving program. (This signal should be used in preference to Signal 9, because the receiving program can catch it and carry out some shutdown operation, for example closing open files; Signal 9 forces the process to die immediately.)
    Trap
       
    一個優秀的程序設計師在設計一個程式時,除了要一些 防呆設計以防止使用者的錯誤使用造成系統的受損之外, 通常在程式的最後面有一些收尾工作需要執行, 例如,將所產生的中間檔案刪除,以免佔用空間, 也把目錄弄得很混亂。可是當一個程序接到作業系統送來的信號時, 必須立刻終止程序,交還 CPU 控制權, 便無法按照正常情況執行收尾的動作,就像建築界的爛尾樓。 為了避免這種問題,Shell 提供一個 trap 指令 給程式設計師使用,trap 會攔截作業系統的信號,交給 shell script,而程式設計師就可以預先設定所欲 攔截的信號以及相應的處理工作。在實際執行 script 時若遇到作業系統送來的信號,就能啟動相關的收尾工作。
       
    Trap 格式如下:
    trap [ command-list ] signo ...
    
    若無 command-list,則沒有對應的動作,等於是忽略所攔截的信號
       
    範例
    trap 'rm -f /tmp/myscript.$$; exit 1' 1 2 3 15
    
    接到信號 1 2 3 15 時將 /tmp/myscript.$$ 檔案刪除
    必須使用 '' 將指令保內的特殊符號護起來,避免一開始就被展開
    Shell 將這個指令儲存起來,等signal 到時,才拿出來執行
    執行時,特殊符號已經沒有''保護,會被shell 展開
    Tue Nov 25 23:14:42 CST 2025 Untitled Document
    nohup 程序的免死金牌與強迫中止
     
    中止一個程序之法
    前景執行的程序 在鍵盤上按下'^C' 或 ''
    背景執行的程序 kill process-id
     
    程序的免死金牌
       
    有兩種方法可以讓一個程序免於被中斷。
    1. 使用trap 攔截中止信號,並忽略之。
    2. 使用 nohup 指令於程序之執行。
      nohup script &
      
     
    程序的強迫中止
       
    kill -9 process-id
    
    這個指令會送一個9號信號給 process-id 強迫中止它, 此時程序的免死金牌失效。
    Tue Nov 25 23:14:42 CST 2025