1、PS1:(提示字元的設定)
這個比較有意思,可以定製自己的提示符;
當我們每次按下 [Enter] 按鍵去執行某個指令後,最後要再次出現提示字元時, 就會主動去讀取這個變數值了。
預設的 bash 的 PS1 變數內的特殊符號代表意義:
o \d :代表日期,格式為 Weekday Month Date,例如 "Mon Aug 1"
o \H :完整的主機名稱。舉例來說,鳥哥的練習機 linux.dmtsai.tw ,那麼這個主機名稱就是 linux.dmtsai.tw
o \h :僅取主機名稱的第一個名字。以上述來講,就是 linux 而已, .dmtsai.tw 被省略。
o \t :顯示時間,為 24 小時格式,如: HH:MM:SS
o \T :顯示時間,12 小時的時間格式!
o \A :顯示時間,24 小時格式, HH:MM
o \u :目前使用者的帳號名稱;
o \v :BASH 的版本資訊;
o \w :完整的工作目錄名稱。家目錄會以 ~ 取代;
o \W :利用 basename 取得工作目錄名稱,所以僅會列出最後一個目錄名。
o \# :下達的第幾個指令。
o \$ :提示字元,如果是 root 時,提示字元為 # ,否則就是 $ 囉~
OK!所以,由預設的 PS1 內容為: '\[\u@\h \W\]\$ ' 就可以瞭解為何我們的提示字元會是: [root@linux ~]# 了吧!好了,那麼假設我想要有類似底下的提示字元:
[root@linux /home/dmtsai 16:50 #12]#
,那個 # 代表第 12 次下達的指令。 那麼應該如何設定 PS1 呢?可以這樣啊:
[root@linux home]# PS1='[\u@\h \w \A #\#]\$ '
[root@linux /home 17:02 #85]#
# 看到了嗎?提示字元變了!變的很有趣吧!其中,那個 #85 比較有趣
2、read
要讀取來自鍵盤輸入的變數,就是用 read 這個指令了。這個指令最常被用在 shell script 的撰寫當中, 以跟使用者進行對談。關於 script 的寫法,在後面章節介紹,底下先來瞧一瞧 read 的相關文法吧!
[root@linux ~]# read [-pt] variable
參數:
-p :後面可以接提示字元!
-t :後面可以接等待的『秒數!』這個比較有趣~不會一直等待使用者啦!
範例:
範例一:讓使用者由鍵盤輸入一內容,將該內容變成 atest 變數
[root@linux ~]# read atest
This is a test
[root@linux ~]# echo $atest
This is a test
範例二:提示使用者 30 秒內輸入自己的大名,將該輸入字串做成 named 變數
[root@linux ~]# read -p "Please keyin your name: " -t 30 named
Please keyin your name: VBird Tsai
[root@linux ~]# echo $named
VBird Tsai
read 之後不加任何參數,直接加上變數名稱,那麼底下就會主動出現一個空白行,等待您輸入。 如果加上 -t 後面接秒數之後,例如上面的範例當中,那麼 30 秒之內沒有任何動作時, 該指令就會自動略過了~如果是加上 -p,就可以增加提示資訊了;
實練補充:
作用
從標準輸入中讀取一行。
文法
read [ -p ][ -r ][ -s ][ -u[ n ] ] [ VariableName?Prompt ]
[ VariableName ... ]
描述
read 命令從標準輸入中讀取一行,並把輸入行的每個欄位的值指定給 shell 變數,用 IFS(內部欄位分隔符號)變數中的字元作為分隔字元。VariableName 參數指定 shell 變數的名稱,shell 變數擷取輸入行一個欄位的值。由VariableName 參數指定的第一個 shell 變數指定給每一個欄位的值,由 VariableName 參數指定的第二個 shell 變數指定給第二個欄位的值,以此類推,直到最後一個欄位。如果標準輸入行的欄位比相應的由 VariableName 參數指定的 shell
變數的個數多,把全部餘下的欄位的值賦給指定的最後的 shell 變數。如果比 shell 變數的個數少,則剩餘的 shell 變數被設定為空白字串。
注意: 如果省略了 VariableName 參數,變數 REPLY 用作預設變數名。
由 read 命令設定的 shell 變數影響當前 shell 執行環境。
標誌
-p 用 |& (管道,& 的記號名稱)讀取由 Korn shell 啟動並執行進程的輸出作為輸入。
註:-p 標誌的檔案結束符引起該進程的清除,因此產生另外一個進程。
-r 指定讀取命令把一個 \ (反斜線) 處理為輸入行的一部分,而不把它作為一個控制字元。
-s 把輸入作為一個命令儲存在 Korn shell 的記錄檔案中。
-u [ n ] 讀取一位元的檔案描述符號碼 n 作為輸入。檔案描述符可以用 ksh exec 內建命令開啟。n 的預設值是 0,表示的是鍵盤。值 2 表示標準錯誤。
參數
VariableName?Prompt 指定一個變數的名稱和一個要使用的提示符。當 Korn shell 是互動式時,它將把提示符寫到標準錯誤,並執行輸入。Prompt 包含多於一個的字,必須用單引號或雙引號括起來。
VariableName... 指定一個或多個由空格分隔的變數名。
退出狀態
這個命令返回下列出口值:
0 成功完成。
>0 檢測到檔案結束符或一個錯誤發生。
樣本
下列指令碼列印一個檔案,這個檔案中每行的第一個欄位被移動到本行的末尾。
while read -r xx yy
do
print printf "%s %s/n" $yy $xx
done < InputFile讀取一行,把它分成欄位,並使用 "Please enter: " 作為提示符,請輸入:
read word1?"Please enter: " word2系統顯示:
Please enter:
You enter:
hello world變數 word1 的值應該是 "hello",變數 word2 應該是 "world."
為建立一個共同進程,用 print -p 寫到共同進程,並用 read -p 從共同進程中讀取輸入,請輸入:
(read; print "hello $REPLY")
print -p "world"
read-p line變數 line 的值應該是 "hello world."
為把輸入行的副本儲存為曆史檔案中的一個命令,請輸入:
read -s line < input_file如果輸入檔案包含 "echo hello world," ,那麼在記錄檔案中將會把 "echo hello world" 儲存為一個命令。
3、 declare / typeset
declare 或 typeset 是一樣的功能,就是在宣告變數的屬性。如果使用 declare 後面並沒有接任何參數, 那麼 bash 就會主動的將所有的變數名稱與內容通通叫出來,就好像使用 set 一樣啦! 那麼 declare 還有什麼文法呢?看看先:
[root@linux ~]# declare [-aixr] variable
參數:
-a :將後面的 variable 定義成為數組 (array)
-i :將後面接的 variable 定義成為整數數字 (integer)
-x :用法與 export 一樣,就是將後面的 variable 變成環境變數;
-r :將一個 variable 的變數設定成為 readonly ,該變數不可被更改內容,也不能 unset
範例:
範例一:讓變數 sum 進行 100+300+50 的加總結果
[root@linux ~]# sum=100+300+50
[root@linux ~]# echo $sum
100+300+50 <==咦!怎麼沒有幫我計算加總?因為這是文字型態的變數屬性啊!
[root@linux ~]# declare -i sum=100+300+50
[root@linux ~]# echo $sum
450 <==瞭乎??
範例二:將 sum 變成環境變數
[root@linux ~]# declare -x sum
範例三:讓 sum 變成唯讀屬性,不可更動!
[root@linux ~]# declare -r sum
[root@linux ~]# sum=tesgting
-bash: sum: readonly variable <==老天爺~不能改這個變數了!
declare 也是個很有用的功能~尤其是當我們需要使用到底下的數組功能時, 他也可以幫我們宣告數組的屬性喔!不過,老話一句,數組也是在 shell script 比較常用的
4、額外的變數設定功能
剛剛我們提到了兩種變數取用的方法,分別是這樣:
[root@linux ~]# echo $HOME
[root@linux ~]# echo ${HOME}
那麼,在那個 ${variable} 的使用方法中,其實,我們還可以將變數進行一些修訂的工作喔! 只要加上一些字元標誌,後面再接著使用比對字串,就能夠修改變數的內容了! 我們取底下的例子來說明:在底下的例子中,假設我的變數名稱為 vbird ,且內容為 /home/vbird/testing/testing.x.sh。
1. 完整呈現 vbird 這個變數的內容;
[root@linux ~]# vbird="/home/vbird/testing/testing.x.sh"
[root@linux ~]# echo ${vbird}
/home/vbird/testing/testing.x.sh
2. 在 vbird 變數中,從最前面開始比對,若開頭為 / ,則刪除兩個 /
之間的所有資料,亦即 /*/
[root@linux ~]# echo ${vbird##/*/}
testing.x.sh <==刪除了 /home/vbird/testing/
[root@linux ~]# echo ${vbird#/*/}
vbird/testing/testing.x.sh <==僅刪除 /home/ 而已
# 這兩個小例子有趣了~變數名稱後面如果接了兩個 ## ,表示在 ##
# 後面的字串取『最長的』那一段;如果僅有一個 # ,表示取『最小的那一段』喔!
3. 承上題,如果是從後面開始,刪除 /* 呢?
[root@linux ~]# echo ${vbird%%/*/}
/home/vbird/testing/testing.x.sh <==都沒被刪除
[root@linux ~]# echo ${vbird%%/*}
<==被刪除光了!
[root@linux ~]# echo ${vbird%/*}
/home/vbird/testing <==只刪除 /testing.x.sh 部分
# 這個例子當中需要特別注意,那個 % 比對的是『最後面那個字元』的意思,
# 所以囉,第一個方式當然不對~因為 vbird 這個變數的內容最後面是 h 而不是 / 啊!
# 至於 %%/* 則是刪除『最長的那個 /* 』,當然就是全部喔!而 %/* 則是最短的那個!
4. 將 vbird 變數中的 testing 取代為 TEST
[root@linux ~]# echo ${vbird/testing/TEST}
/home/vbird/TEST/testing.x.sh
[root@linux ~]# echo ${vbird//testing/TEST}
/home/vbird/TEST/TEST.x.sh
# 如果變數後面接的是 / 時,那麼表示後面是進行『取代』的工作~而且僅取代『第一個』
# 但如果是 // ,則表示全部的字串都取代啊!
這裡您稍微留意一下就好了~反正就是變數後面可以接 #, ##, %, %%, /, // , 而他們存在的意義並不相同的啦~ 另外,幾個不同的變數內容還可以進行判斷呢! 舉例來說,目前我需要用到兩個變數,分別是 var 與 str , 那我想要針對 str 這個變數內容是否有設定成一個字串,亦即 "expr" 來決定 var 的內容。 那我可以使用什麼方法來進行判斷呢?如果您會寫 shell script 的話, 直接用 shell script 就好了,如果不會寫,那麼我們就透過簡單的變數判斷吧!
Tips: 底下的例子當中,那個 var 與 str 為變數,我們想要針對 str 是否有設定來決定 var 的值喔! 一般來說, str: 代表『str 沒設定或為空白的字串時』;至於 str 則僅為『沒有該變數』。
變數設定方式 str 沒有設定 str 為空白字串 str 已設定非為空白字串
var=${str-expr} var=expr var= var=$str
var=${str:-expr} var=expr var=expr var=$str
var=${str+expr} var=expr var=expr var=expr
var=${str:+expr} var=expr var= var=expr
var=${str=expr} str=expr;var=expr str 不變 var= str 不變 var=$str
var=${str:=expr} str=expr var=expr str=expr var=expr str 不變 var=$str
var=${str?expr} expr 輸出至 stderr var= var=str
var=${str:?expr} expr 輸出至 stderr expr 輸出至 stderr var=str
根據上面這張表,我們來進行幾個範例的練習
範例一:若 str 這個變數內容存在,則 var 設定為 str ,否則 var 設定為 "newvar"
[root@linux ~]# unset str; var=${str-newvar}
[root@linux ~]# echo var="$var", str="$str"
var=newvar, str= <==因為 str 不存在,所以 var 為 newvar
[root@linux ~]# str="oldvar"; var=${str-newvar}
[root@linux ~]# echo var="$var", str="$str"
var=oldvar, str=oldvar <==因為 str 存在,所以 var 等於 str 的內容
範例二:若 str 不存在,則 var 與 str 均設定為 newvar,否則僅 var 為 newvar
[root@linux ~]# unset str; var=${str=newvar}
[root@linux ~]# echo var="$var", str="$str"
var=newvar, str=newvar <==因為 str 不存在,所以 var/str 均為 newvar
[root@linux ~]# str="oldvar"; var=${str=newvar}
[root@linux ~]# echo var="$var", str="$str"
var=oldvar, str=oldvar <==因為 str 存在,所以 var 等於 str 的內容
範例三:若 str 這個變數存在,則 var 等於 str ,否則輸出 "novar"
[root@linux ~]# unset str; var=${str?novar}
-bash: str: novar <==因為 str 不存在,所以輸出錯誤訊息
[root@linux ~]# str="oldvar"; var=${str?novar}
[root@linux ~]# echo var="$var", str="$str"
var=oldvar, str=oldvar <==因為 str 存在,所以 var 等於 str 的內容
# 上面這三個案例都沒有提到當 str 有設定,且為空白字串的情況喔!
# 您可以自行測試一下哩!
雖然猛一看,覺得變數沒有什麼奇特的地方,但是,如果仔細瞧一瞧,嘿!一堆環境變數與系統資源方面的變數, 可是會影響到我們在 bash 裡頭是否能夠順利作業的呢!例如 PATH 啊、ulimit 之類的~
特殊變數:
$* 這個程式的所有參數 如命令ls -al dir 即代表 -al dir
$? 執行上一個命令的傳回值 為0表示執行成功!
$! 執行上一個後台命令的PID
$$ 這個程式的PID
$# 這個程式的參數個數
如命令ls -al dir 即代表2個
5、別名
增加別名:
Loong:/home/yee/shell# alias vi="vim"
Loong:/home/yee/shell# lm
bash: lm: command not found
Loong:/home/yee/shell# alias lm="vim"
Loong:/home/yee/shell# lm kkk.txt
取消別名:
Loong:/home/yee/shell# una
unalias uname
Loong:/home/yee/shell# unalias lm
Loong:/home/yee/shell# lm
bash: lm: command not found
Loong:/home/yee/shell#
6、萬用字元與特殊符號:
!在 bash 裡頭還支援一些萬用字元喔 (wild card) !多了這些萬用字元, 我們利用 bash 處理資料就更方便了!底下我們列出一些常用的萬用字元喔:
符號
內容
- *
- 萬用字元,代表 0 個或多個字元(或數字)
- ?
- 萬用字元,代表『一定有』一個字母
- #
- 批註,這個最常被使用在 script 當中,視為說明!
- \
- 跳脫符號,將『特殊字元或萬用字元』還原成一般字元
- |
- 分隔兩個管線命令的界定;
- ;
- 連續性命令的界定(注意!與管線命令並不相同)
- ~
- 使用者的家目錄
- $
- 亦即是變數之前需要加的變數取代值
- &
- 將指令變成背景下工作
- !
- 邏輯運算意義上的『非』 not 的意思!
- /
- 路徑分隔的符號
- >, >>
- 輸出導向,分別是『取代』與『累加』
- '
- 單引號,不具有變數置換的功能
- "
- 具有變數置換的功能!
- ` `
- 兩個『 ` 』中間為可以先執行的指令!
- ( )
- 在中間為子 shell 的起始與結束
- [ ]
- 在中間為字元的組合
- { }
- 在中間為命令區塊的組合!
- 組合按鍵
- 執行結果
- Ctrl + C
- 終止目前的命令
- Ctrl + D
- 輸入結束(EOF),例如郵件結束的時候;
- Ctrl + M
- 就是 Enter 啦!
- Ctrl + S
- 暫停螢幕的輸出
- Ctrl + Q
- 恢複螢幕的輸出
- Ctrl + U
- 在提示字元下,將整列命令刪除
- Ctrl + Z
- 『暫停』目前的命令
[root@linux ~]# ls test* <==那個 * 代表後面不論接幾個字元都予以接受
[root@linux ~]# ls test? <==那個 ? 代表後面『一定』要接『一個』字元
[root@linux ~]# ls test??? <==那個 ??? 代表『一定要接三個』字元!
[root@linux ~]# cp test[1-5] /tmp
# 將 test1, test2, test3, test4, test5 若存在的話,就拷貝到 /tmp
[root@linux ~]# cp test[!1-5] /tmp
# 只要不是 test1, test2, test3, test4, test5 之外的其它 test? ,
# 若存在的話,就拷貝到 /tmp
[root@linux ~]# cd /lib/modules/`uname -r`/kernel/drivers
# 被 ` ` 括起來的內容『會先執行』
上面幾個例子相當的有趣!尤其是最後面兩個!需要注意的是, [1-5] 裡面『代表只有一個字元』但是範圍可以由 1-5 ,這樣來說的話,那麼我們如果允許『只要檔名裡面含有至少一個大寫字元』時,就可以將檔案 copy 出來的話,可以這樣做:
cp *[A-Z]* /tmp
很有趣吧?!也就是說『 [ ] 謹代表一個字元,而這個字元的定義可以是範圍(-), 可以是指定項目,也可以是兩者並存。 』舉例來說,我想要找出在 /etc/ 底下所有含有數位檔案, 可以這樣:
ls -lda /etc/*[0-9]*
但如果我只想要找出含有 3 及 5 的檔名的檔案呢?就會是這樣:
ls -lda /etc/*[35]*
如果是『不想要』某些範圍或者是單字呢?就使用 [!] 即可!例如不想要有小寫字元為開頭的檔案:
ls -lda /etc/[!a-z]*
很好玩吧!至於那個 ` 是啥?在一串指令當中, `command` 內的指令會先被執行, 執行完的訊息再回傳到外部指令來處理!也就是說:
1. 系統先執行 uname -r 找出輸出的結果;
2. 將結果累加在目錄上面,來執行 cd 的功能!
很棒吧!!另外,這個 quot (`) 的功能,也可以利用 $() 來取代喔!例如:
cd /lib/modules/$(uname -r)/kernel
這些基本的功能需要特別來瞭解一下才行
內容摘自:《鳥哥私房菜》