找回密码
 注册
查看: 2919|回复: 1

認識 BASH Shell zz

[复制链接]
发表于 2006-10-21 00:28:55 | 显示全部楼层 |阅读模式
zz:鳥哥的 Linux 私房菜
               
       
認識 BASH Shell


最近更新日期:2006/10/05

文字模式 (command line) 這種指令下達的方式,在 Linux 裡面,其實就相當於是 bash 的工具與介面! 因為 Linux 就是以 bash 為預設的 shell 的!那麼前幾章我們都已經很快樂的進行了很多的指令下達囉~ 所以說, bash shell
根本就不難吧~是啦!只要能夠熟悉的話,那麼確實他也不是這麼不可親近的一項工具啊~ 這個章節中,鳥哥會由變數談起,先講到環境變數的功能與修改的問題, 然後會繼續提到歷史指令的運用。接下來,就會談一下『資料流重導向』這個重要概念,
最後就是管線命令的利用啦!好好清一清腦門,準備用功去囉~ ^_^ 這個章節幾乎是所有 command line 與未來主機維護與管理的重要基礎,一定要好好仔細的閱讀喔!

1. Bash shell
  1.1 什麼是 shell ?
  1.2 系統的 shell 與 /etc/shells 功能
  1.3 Bash shell 的功能
  1.4 Bash shell 的內建命令: type
  1.5 指令的下達
2. Shell 的變數功能
  2.1 變數的取用與設定:echo, 變數設定規則, unset
  2.2 變數的用途?
  2.3 環境變數的功能: env, 一些重要的環境變數, set, export
  2.4 語系檔案的變數 (locale)
  2.5 變數的有效範圍:
  2.6 變數鍵盤讀取、陣列與宣告: read, declare, array
  2.7 與檔案系統及程序的限制關係: ulimit
  2.8 其他額外變數功能
3. 命令別名與歷史命令:
  3.1 命令別名設定: alias, unalias
  3.2 歷史命令: history, HISTSIZE
4. Bash shell 使用環境:
  4.1 絕對路徑與相對路徑
  4.2 登錄訊息顯示資料: /etc/issue, /etc/motd
  4.3 環境設定檔: bashrc, ~/.bashrc, ~/.profile, profile...,/etc/inputrc, source
  4.4 終端機的環境設定: stty, set
  4.5 萬用字元與特殊符號:
5. 資料流重導向 (redirecte)
  5.1 何謂資料流重導向?
  5.2 命令執行的判斷依據: ; , &&, ||
6. 管線命令 (pipe):
  6.1 擷取命令: cut, grep
  6.2 排序命令: sort, wc, uniq
  6.3 雙向重導向: tee
  6.4 字元轉換命令: tr, col, join, paste, expand
  6.5 分割命令: split
  6.6 參數代換: xargs
  6.7 關於減號 - 的用途
7. 本章習題練習
8. 針對本文的建議:http://phorum.vbird.org/viewtopic.php?t=23884
大標題的圖示Bash shell
我們在前面的 什麼是 Linux 那個章節當中,提到了, 管理整個硬體的其實是核心 (kernel),那我們一般使用者 (user) 則是以 shell 來跟核心溝通~ 讓核心達到我們所想要達到的工作目的。那麼系統有多少 shell 可用呢? 為什麼我們要使用 bash 啊?!底下分別來談一談喔!

小標題的圖示什麼是 Shell?
這應該是個蠻有趣的話題:『什麼是 Shell ?』相信只要摸過電腦,對於作業系統 ( 不論是 Linux 、 Unix 或者是 Windows ) 有點概念的朋友們大多聽過這個名詞,因為只要有『作業系統』那麼就離不開 Shell 這個東西。不過,在討論 Shell
之前,我們先來瞭解一下電腦的運作狀況吧! 舉個例子來說:當你要電腦傳輸出來『音樂』的時候,你的電腦需要什麼東西呢?

   1. 當然就是需要你的硬體有『音效卡晶片』這個硬體配備,否則怎麼會有聲音;
   2. 作業系統的核心可以支援這個晶片組,當然還需要提供晶片的驅動程式囉;
   3. 需要使用者(就是你)輸入發生聲音的指令囉!

這就是基本的一個輸出聲音的需要的步驟!那麼也就是說,你必須要『輸入』一個指令之後, 『硬體』才會透過你下達的指令來工作!嘿嘿!那麼硬體如何知道你下達的指令呢?那就是 kernel (核心)的控制工作了!瞭解了嗎?沒錯!也就是說,我們必須要透過『 Shell
』將我們輸入的指令與 Kernel 溝通,好讓 Kernel 可以控制硬體來正確無誤的工作! 基本上,我們可以透過底下這兩張圖來說明一下:

硬體、核心與使用者的相關性圖示
圖一、硬體、核心與使用者的相關性圖示

硬體、核心與使用者的相關性圖示
圖二、硬體、核心與使用者的相關性圖示

基本上,替我們工作的是『硬體』,而控制硬體的是『核心』,再來,我們使用者乃是利用『Shell』控制一些 kernel 提供的 『工具 (Utility)』來操控硬體替我們正確的工作。再進一步來說,由於 kernel 聽不懂人類的語言,而人類也沒有辦法直接記得 kernel
的語言,所以兩者的溝通就得藉由 shell 來支援了!(其實早期的 DOS 的文字介面也是使用 shell 來溝通呀!那個 shell 的名稱就叫做 command.com ,還記得嗎? ^_^)

以字面上的意思來說, kernel 是『核心』的意思,而 Shell 是『殼』的意思,呵呵!也就是說, shell 是最外頭的咚咚!而 kernel 乃是最內層的的咚咚啦!核心是作業系統的最底層的東西! 這個核心裡頭包括了各種的支援硬體的工具!當然囉,如果你的硬體太新,而你的 kernel
並沒有支援的話,那麼很抱歉,你的 Shell 能力再怎麼強,也沒有辦法使硬體工作的! 這樣可以瞭解了嗎?呵呵!沒錯!使電腦主機工作的正是核心的任務,但是操作核心來替使用者工作的,卻是 shell 喔!因此,有時候你的 shell 搞了老半天,硬體卻不能工作的時候,請注意,
您的『核心』是否正確呢?阿!扯遠了!這是 kernel 章節才要說的東西。

# 我幹嘛要學習文字模式的 Shell 呢?
我們常常提到的 shell 其實是比較狹隘的定義,一般來說,在 Linux 裡頭,所謂的 shell 就是指 BASH 這個文字模式的 shell 囉。但是,廣義的 shell 也可以是 KDE 之類的圖形介面控制軟體呢! 因為他也可以幫我們與 kernel 進行溝通啊!不過,在鳥哥的 Linux 私房菜裡面,
如果沒有特別說明的話,那麼我們的 shell 指的是比較狹義的,也就是文字模式的 shell 喔!

另外,鳥哥常常聽到這個問題:『我幹嘛要學習 shell 呢? 不是已經有很多的工具可以提供我設定我的主機了?我為何要花這麼多時間去學指令呢?不是以 X Window 按一按幾個按鈕就可以搞定了嗎?為什麼要這麼麻煩?』唉~還是得一再地強調, X Window 還有 Web
介面的設定工具例如 webmin 是真的好用的傢伙, 他真的可以幫助我們很簡易的設定好我們的主機,甚至是一些很進階的設定都可以幫我們搞定。

但是鳥哥在序章裡面也已經提到過相當多次了, X Window 的介面雖然親善,功能雖然強大,而 web 介面的工具也可以提供我們很友善的服務,但是畢竟他是將所有利用到的套件都整合在一起的一個套件而已,
並非是一個完整的套件,所以某些時候當你升級或者是使用其他套件管理模組( 例如 tarball 而非 rpm 檔案等等 )時,就會造成設定的困擾了。

此外,遠端連線時,文字介面的傳輸速度一定比較快, 而且,較不容易出現斷線或者是資訊外流的問題,因此, shell 真的是得學習的一項工具。而且,他可以讓您更深入 Linux ,更瞭解他, 而不是只會按一按滑鼠而已!所謂『天助自助者!』多摸一點文字模式的東西,會讓你與
Linux 更親近呢!

有些朋友也很可愛,常會說:『我學這麼多幹什麼? 又不常用,也用不到!』嘿嘿!有沒有聽過『書到用時方恨少?』 當你的主機一切安然無恙的時候,您當然會覺得好像學這麼多的東西一點幫助也沒有呀! 萬一,某一天真的不幸給他中標了,您該如何是好?是直接重新安裝?
還是先追蹤入侵來源後進行漏洞的修補?或者是乾脆就關站好了?這當然涉及很多的考量, 但就以鳥哥的觀點來看,多學一點總是好的,尤其我們可以有備而無患嘛!甚至學的不精也沒有關係,瞭解概念也就 OK 啦!畢竟沒有人要您一定要背這麼多的內容啦!瞭解概念就很了不起了!

此外,如果您真的有心想要將您的主機管理的好,那麼良好的 shell 程式編寫是一定需要的啦!就鳥哥自己來說,我管理的主機雖然還不算多, 只有區區不到十部,但是如果每部主機都要花上幾十分鐘來查閱他的 log file
以及相關的資訊,那麼我可能會瘋掉!基本上,也太沒有效率了!這個時候,如果能夠藉由 shell 提供的命令重導向( 或稱資料流重導向 ),以及管線命令,呵呵!那麼我分析 log file 只要花費不到十分鐘就可以看完所有的主機之重要資訊了!相當的好用呢!

由於學習 shell 的好處真的是多多啦!所以,如果您是個系統管理員,或者有心想要管理系統的話,那麼 shell 這個東西與 shell scripts 這個東西,真的真的有必要看一看!
小標題的圖示系統的 shell 與 /etc/shells 功能
知道什麼是 Shell 之後,那麼我們來瞭解一下 Linux 使用的是哪一個 shell 呢?什麼!哪一個?難道說 shell 不就是『一個 shell 嗎?』哈哈!那可不!由於早年的 Unix 年代,發展者眾,所以由於 shell 依據發展者的不同就有許多的版本,例如常聽到的 Bourne SHell (sh)
、在 Sun 裡頭預設的 C SHell、 商業上常用的 K SHell、, 還有 TCSH 等等,每一種 Shell 都各有其特點。至於 Linux 使用的這一種版本就稱為『 Bourne Again SHell (簡稱 bash) 』,這個 Shell 是 Bourne Shell 的增強版本,也是基準於 GNU 的架構下發展出來的呦!

在介紹 shell 的優點之前,先來說一說 shell 的簡單歷史吧:第一個流行的 shell 是由 Steven Bourne 發展出來的,為了紀念他所以就稱為 Bourne shell ,或直接簡稱為 sh !而後來另一個廣為流傳的 shell 是由柏克萊大學的 Bill Joy 設計依附於 BSD 版的 Unix 系統中的
shell ,這個 shell 的語法有點類似 C 語言,所以才得名為 C shell ,簡稱為 csh !由於在學術界 Sun 主機勢力相當的龐大,而 Sun 主要是 BSD 的分支之一,所以 C shell 也是另一個很重要而且流傳很廣的 shell 之一 ( 因為太多的程式設計師使用的就是 C 語言啦!
)!(還記得我們在 Linux 是什麼那一章提到的吧? Sun 公司的創始人就是 Bill Joy,而 BSD 最早就是 Bill Joy 發展出來的啊!)。

那麼目前我們的 Linux (以 FC4 為例) 有多少我們可以使用的 shells 呢? 你可以檢查一下 /etc/shells 這個檔案,至少就有底下這幾個可以用的 shells:

    * /bin/sh (已經被 /bin/bash 所取代)
    * /bin/bash (就是 Linux 預設的 shell)
    * /bin/ksh (Kornshell 由 AT&T Bell lab. 發展出來的,相容於 bash)
    * /bin/tcsh (整合 C Shell ,提供更多的功能)
    * /bin/csh (已經被 /bin/tcsh 所取代)
    * /bin/zsh (基於 ksh 發展出來的,功能更強大的 shell)

由上面的說明中,我們大概可以發現,其實各主要 shell 的功能都差不多, 有的只是語法上面的不同而已。目前一般的使用者使用習慣上,似乎是以 bash 及 csh 為主要的兩個 shell 。OK!這麼多的 shell 我要使用哪一個啊?呵呵!使用 Linux 支援最廣泛的 bash 就好了!
不要想太多!另外,咦!為什麼我們系統上的 shell 要寫入 /etc/shells 這個檔案啊? 這是因為系統某些服務在運行過程中, 會去檢查使用者能夠使用的 shells ,而這些 shell 的查詢就是藉由 /etc/shells 這個檔案囉!

舉例來說,某些 FTP 網站會去檢查使用者的可用 shell ,而如果你不想要讓這些使用者使用 FTP 以外的主機資源時,可能會給予該使用者一些怪怪的 shell,讓使用者無法以其他服務登入主機。 這個時候,你就得將那些怪怪的 shell 寫到 /etc/shells 當中了。舉例來說,我們的
FC4 的 /etc/shells 裡頭就有個 /sbin/nologin 檔案的存在,這個就是我們說的怪怪的 shell 囉~

那麼,再想一想,我這個使用者什麼時候可以取得 shell 來工作呢?還有, 我這個使用者預設會取得哪一個 shell 啊?!還記得我們在 首次進入 Linux -- 以文字方式登入 那個章節當中提到的登入動作吧?當我登入的時候,系統就會給我一個 shell 讓我來工作了。
而這個登入取得的 shell 就記錄在 /etc/passwd 這個檔案內!這個檔案的內容是啥?

[root@linux ~]# cat /etc/passwd
root:0:0:root:/root:/bin/bash
bin:1:1:bin:/bin:/sbin/nologin
daemon:2:2:daemon:/sbin:/sbin/nologin
....(中間省略).....

如上所示,在每一行的最後一個資料,就是您登入後,可以取得的預設的 shell 啦! 那你也會看到, root 是 /bin/bash ,不過,系統帳號 bin 與 daemon 等等,就使用那個怪怪的 /sbin/nologin 囉~關於使用者這部分的內容,我們留在 帳號管理 時提供更多的說明。

小標題的圖示Bash shell 的功能
既然 /bin/bash 是 Linux 預設的 shell ,那麼總是得瞭解一下這個玩意兒吧! BASH 是怎麼一回事呢?這個 shell 是 GNU 計畫中重要的工具軟體之一,目前也是 GNU 作業系統中標準的 shell ,他主要相容於 sh ,並且依據一些使用者需求,而加強的 shell
版本,可以說目前幾乎所有的 Linux distribution 都是使用 bash 作為管理核心的主要 shell !因此,不論您使用的是那個 distribution ,你都難逃需要學習 bash 的宿命啦!那麼這個 shell 有什麼好處,幹嘛 Linux 要使用他作為預設的 shell 呢? BASH
主要的優點有底下幾個:

# 命令編修能力(類似 DOS 的 doskey 功能):
使用 bash 裡頭,個人認為相當棒的一個功能就是『他能記憶使用過的指令!』 這功能真的相當的棒!因為我只要在指令列按『上下鍵』就可以找到前一個輸入的指令!而在很多 distribution 裡頭,預設的指令記憶功能可以到達 1000 個!也就是說,
你曾經下達過的指令都被記錄下來了,記錄的檔案在你的家目錄內的 .bash_history !不過,需要留意的是, ~/.bash_history 記錄的是前一次登入以前所執行過的指令, 而至於這一次登入所執行的指令都被暫存在暫記憶體中,當您成功的登出系統後,該指令記憶才會記錄到
.bash_history 當中!

這有什麼功能呢?最大的好處就是可以『查詢曾經做過的舉動!』, 如此可以知道你的執行步驟,那麼就可以追蹤您曾下達的指令,以作為除錯的工具! 但如此一來也有個煩惱,就是如果被駭客入侵了,那麼他只要翻你曾經執行過的指令, 剛好你的指令又跟系統有關(例如直接輸入
MySQL 的密碼在指令列上面)那麼很容易就被破解你的 Linux 主機!所以,最好是將記錄的指令數目減小一點較好!

# 命令與檔案補全功能:
還記得我們在 首次進入 Linux 的熱門按鍵 一節當中提到的 [tab] 這個按鍵嗎?!這個按鍵的功能就是在 bash 裡頭才有的啦!常常在 bash 環境中使用 [tab] 是個很棒的習慣喔!因為至少可以讓你 1)少打很多字; 2)確定輸入的資料是正確的! 使用 [tab] 按鍵的時機依據 [tab]
接在指令後或參數後而有所不同。我們再複習一次:

    * [Tab] 接在一串指令的第一個字的後面,則為命令補全;
    * [Tab] 接在一串指令的第二個字以後時,則為『檔案補齊』!

所以說,如果我想要知道我的環境中,所有可以執行的指令有幾個? 就直接在 bash 的提示字元後面輸入兩個 [tab][tab] 就能夠輸出所有的可執行指令了。 那如果想要知道系統當中所有以 c 為開頭的指令呢?就按下 c[tab][tab] 就好啦! ^_^

是的!真的是很方便的功能,所以,有事沒事,在 bash shell 底下,多按幾次 [tab] 是一個不錯的習慣啦!

# 命令別名(alias)設定功能:
假如我需要知道這個目錄底下的所有檔案(包含隱藏檔)及所有的檔案屬性,那麼我就必須要下達 ls -al 這樣的指令列,唉!真麻煩,有沒有更快的取代方式?呵呵!就使用命令別名呀!例如我最喜歡直接以 lm 這個自訂的命令來取代上面的命令,也就是說, lm 會等於 ls -al
這樣的一個功能,嘿!那麼要如何作呢?就使用 alias 即可!你可以在指令列輸入 alias 就可以知道目前的命令別名有哪些了!也可以直接下達命令來設定別名呦:

      alias lm='ls -al'

# 工作控制(jobs)、前景背景控制:
這部分我們在之後的資源管理章節中會再提及! 使用前、背景的控制可以讓工作進行的更為順利!至於工作控制(jobs)的用途則更廣, 可以讓我們隨時將工作丟到背景中執行!而不怕不小心使用了 [Ctrl] + c
來停掉該程序!真是好樣的!此外,也可以在單一登入的環境中,達到多工的目的呢!

# Shell scripts 的強大功能:
在 DOS 年代還記得將一堆指令寫在一起的所謂的『批次檔』吧?在 Linux 底下的 shell scripts 則發揮的更為強大的功能,可以將您日常生活當中常需要下達的連續指令寫成一個檔案, 該檔案並且可以透過對談互動式的方式來進行主機的偵測工作!也可以藉由 shell
提供的環境變數及相關指令來進行設計,哇!整個設計下來幾乎就是一個小型的程式語言了!該 scripts 的功能真的是超乎我的想像之外!以前在 DOS 底下需要程式語言才能寫的東西,在 Linux 底下使用簡單的 shell scripts
就可以幫你達成了!真的厲害!!這部分我們在後續章節再來談!

# 萬用字元!
除了完整的字串之外, bash 還支援許多的萬用字元來幫助使用者查詢與指令下達。 舉例來說,想要知道 /usr/X11R6/bin 底下有多少以 xt 為開頭的檔案嗎?使用: ls -l /usr/X11R6/bin/xt* 就能夠知道囉~此外,還有其他可供利用的萬用字元, 這些都能夠加快使用者的操作呢!
小標題的圖示Bash shell 的內建命令: type
我們在首次進入 Linux 章節當中,提到關於 Linux 的線上說明文件 部分,也就是 man page 的內容,那麼 bash 有沒有什麼說明文件啊?開玩笑~ 這麼棒的東西怎麼可能沒有說明文件!請您在 shell 的環境下,直接輸入 man bash 瞧一瞧,
嘿嘿!不是蓋的吧!讓您看個幾天幾夜也無法看完的 bash 說明文件,可是很詳盡的資料啊! ^_^

不過,在這個 man bash 所出現的 man page 當中,不知道您是否有察覺到,咦! 怎麼這個說明文件裡面有其他的檔案說明啊?舉例來說,那個 cd 指令的說明就在這個 man page 內? 然後我直接輸入 man cd
時,怎麼出現的畫面中,最上方竟然出現一堆指令的介紹??這是怎麼回事? 為了方便 shell 的操作,其實 bash 已經『內建』了很多指令了,例如上面提到的 cd , 還有例如 umask 等等的指令,都是內建在 bash 當中的呢!

那我怎麼知道這個指令是來自於外部指令(指的是其他非 bash 套件所提供的指令) 或是內建在 bash 當中的呢? 嘿嘿!利用 type 這個指令來觀察即可!舉例來說:

[root@linux ~]# type [-tpa] name
參數:
    :不加任何參數時,則 type 會顯示出那個 name 是外部指令還是 bash 內建的指令!
-t  :當加入 -t 參數時,type 會將 name 以底下這些字眼顯示出他的意義:
      file    :表示為外部指令;
      alias   :表示該指令為命令別名所設定的名稱;
      builtin :表示該指令為 bash 內建的指令功能;
-p  :如果後面接的 name 為指令時,會顯示完整檔名(外部指令)或顯示為內建指令;
-a  :會將由 PATH 變數定義的路徑中,將所有含有 name 的指令都列出來,包含 alias
範例:
範例一:查詢一下 ls 這個指令是否為 bash 內建?
[root@linux ~]# type ls
ls is aliased to `ls --color=tty'
# 沒有加上任何參數,僅列出 ls 這個指令的最主要使用情況
[root@linux ~]# type -t ls
alias
# -t 參數則僅列出 ls 這個指令的最主要使用情況說明
[root@linux ~]# type -a ls
ls is aliased to `ls --color=tty'
ls is /bin/ls
# 利用所有方法找出來的 ls 相關資訊都會被列出來!

範例二:那麼 cd 呢?
[root@linux ~]# type cd
cd is a shell builtin

透過 type 這個指令的用途,我們可以知道每個指令是否為 bash 的內建指令。 此外,由於利用 type 搜尋後面的名稱時,如果後面接的名稱並不能以執行檔的狀態被找到, 那麼該名稱是不會被顯示出來的。舉例來說,您的 FC4 應該不會有 vbird 這個指令吧?! 輸入 type -p
vbird 看一下,果然沒有輸出任何資料!而如果您輸入的是 type -p touch 呢? 則會出現 /bin/touch !呵呵!所以,這個 type 也可以用來作為類似 which 指令的用途啦!找指令用的!

小標題的圖示指令的下達
我們在 首次進入 Linux 一節當中,已經提到過在 shell 環境下的指令下達方式,不過,因為這個部分實在很重要,所以,我們還是再次的提醒一次!

[root@linux ~]# command [-options] parameter1 parameter2 ...
                  指令     選項      參數(1)    參數(2)
說明:
0. 一行指令中第一個輸入的絕對是『指令(command)』或『可執行檔案』
1. command 為指令的名稱,例如變換路徑的指令為 cd 等等;
2. 中刮號[]並不存在於實際的指令中,而加入參數設定時,通常為 - 號,例如 -h;
   有時候完整參數名稱會輸入 -- 符號,例如 --help;
3. parameter1 parameter2.. 為依附在 option 後面的參數,
   或者是 command 的參數;
4. command, -options, parameter1.. 這幾個咚咚中間以空格來區分,
   不論空幾格 shell 都視為一格;
5. 按下 [Enter] 按鍵後,該指令就立即執行。[Enter] 按鍵為 <CR> 字符,
   他代表著一行指令的開始啟動。
6. 指令太長的時候,可以使用 \ 符號來跳脫 [Enter] 符號,
   使指令連續到下一行。注意! \ 後就立刻接特殊字符。
7. 在 Linux 系統中,英文大小寫字母是不一樣的。舉例來說, cd 與 CD 並不同。
範例:

範例一:列出 /root 底下的各檔案名稱
[root@linux ~]# ls -al /root
[root@linux ~]# ls     -al      /root
# 不論指令與參數中間空幾格,都是可以接受的!

範例二:如果指令太長的話,如何使用兩行來輸出?
[root@linux ~]# cp /var/spool/mail/root /etc/crontab \
> > /etc/fstab /root
# 上面這個指令,就是將三個檔案複製到 /root 這個目錄下而已。不過,因為指令太長,
# 於是鳥哥就利用 \[Enter] 來將 [Enter] 這個按鍵『跳脫!』開來,讓
# [Enter] 按鍵不再具有上述說明的第 5 點功能!好讓指令繼續在下一行輸入。
# 需要特別留意, [Enter] 按鍵是緊接著反斜線 (\) 的,兩者中間沒有其他字元。
# 因為 \ 僅跳脫『緊接著的下一個字符』而已!所以,萬一我寫成:
# \ [Enter] ,亦即 [Enter] 與反斜線中間有一個空格時,則 \ 跳脫的是『空白鍵』
# 而不是 [Enter] 按鍵!這個地方請在仔細的看一遍!很重要!
# 如果順利跳脫 [Enter] 後,下一行最前面就會主動出現 > 的符號,
# 您可以繼續輸入指令囉!也就是說,那個 > 是系統自動出現的,你不需要輸入。

總之,當我們順利的在終端機 (tty) 上面登入後, Linux 就會依據 /etc/passwd 檔案的設定給我們一個 shell ,預設就是 bash ,然後我們就可以依據上面的指令下達方式來操作 shell, 之後,我們就可以透過 man 這個線上查詢來查詢指令的使用方式與參數說明,
很不錯吧!那麼我們就趕緊更進一步來操作 bash 這個好玩的東西囉!
大標題的圖示Shell 的變數功能
在繼續研究 BASH 之前,我們得要先就 變數 這個東西來討論一番。 為什麼要討論變數呢?又,變數是啥玩意兒啊?!先來談一談國中數學好了,您是否依稀記得, 我們國中時候學過所謂的『 y = ax + b 』這東西?其中, y 是變數, x 則是這個變數的內容啊!
講的更簡單一點,我們可以『用一個簡單的 "字眼" 來取代另一個比較複雜或者是容易變動的資料』。這有什麼好處啊?最大的好處就是『方便!』。

如果以 Linux 主機的運作來說明好了,因為在主機裡面有太多的資料需要進行存取了, 而這些資料都是一些服務所必須的,例如某個名為 dmtsai 的帳號,他的 mail 的存取路徑預設是在 /var/spool/mail/dmtsai 、家目錄預設在 /home/dmtsai 等等。那如果換了另外一個帳號呢?
假設另一個帳號名稱為 vbird ,你猜他的郵件與家目錄在哪?應該是在 /var/spool/mail/vbird 與 /home/vbird 對吧! 那麼我們主機的郵件服務是否要記錄好幾個不同的路徑啊?會不會太麻煩?這當然很麻煩囉~
所以為了簡化整個運作流程,我們就可以透過某個變數功能,讓這個變數可以依據不同的使用者而變更內容, 如此一來,系統的郵件服務只要依據那個變數去取得所需要的資料即可,就不需要記錄不同的路徑囉。

舉例來說,我們每個帳號的郵件信箱預設是以 MAIL 這個變數來進行存取的, 當 dmtsai 這個使用者登入時,他便會取得 MAIL 這個變數,而這個變數的內容其實就是 /var/spool/mail/dmtsai, 那如果 vbird 登入呢?他取得的 MAIL 這個變數的內容其實就是 /var/spool/mail/vbird
。 而我們使用信件讀取指令 mail 來讀取自己的郵件信箱時,嘿嘿,這支程式可以直接讀取 MAIL 這個變數的內容, 就能夠自動的分辨出屬於自己的信箱信件囉!這樣一來,設計程式的設計師就真的很方便的啦!

當然我們可以改變這些個變數,但是如果該變數是直接深植於套件當中, 那麼當你修改了某些參數之後,嘿嘿!你的套件就必須要『由原始碼直接更新再編譯』 才行!這樣似乎很麻煩,所以囉,變數真的是很方便的啦!
Tips:
舉個簡單的例子來說, sendmail 的 smtp 存放 mail 路徑是經由 /etc/profile 裡頭的:

      MAIL="/var/spool/mail/$USER"

來設定的,而當我修改了上面這一個咚咚,然後重新開機之後,嘿嘿嘿嘿! 我的郵件就可以存放到不同的路徑去了!而且不會有問題!可以順利的『在 Linux 主機上面』收發。然而問題發生在 pop3 這個服務上面,由於 pop3 的預設路徑是在 source code 裡頭,而且就正是
/var/spool/mail 這個路徑,也就是說,不論我怎麼修正我的『變數』, pop3 都不為所動!唉~真慘,所以就無法直接以 pop3 來收信了(例如 OutLook 就不能工作了)!會發生密碼不接受的問題呢!     
再來繼續講到其他的變數功能好了,我們前面已經提到過很多次,能不能執行某個指令, 與 PATH 這個變數也有很大的關係的。舉例來說,我們在任何地方下達 ls 這個指令時,系統就是透過 PATH 這個變數裡面的內容所記錄的路徑順序來搜尋指令的呢!如果在搜尋完 PATH
變數內的路徑還找不到 ls 這個指令時, 就會在螢幕上顯示『 command not found 』的錯誤訊息了。

這些還都只是系統預設的變數的目的,如果是個人的設定方面的應用呢:例如你要寫一個大型的 script (批次檔)時,有些資料因為可能由於使用者習慣的不同而有差異,比如說路徑好了,由於該路徑在 script 被使用在相當多的地方,如果下次換了一部主機,都要修改 script
裡面的所有路徑,那麼我一定會瘋掉! 這個時候如果使用變數,而將該變數的定義寫在最前面,後面相關的路徑名稱都以變數來取代, 嘿嘿!那麼你只要修改一行就等於修改整篇 script 了!方便的很!所以,良好的程式設計師都會善用變數的定義! ( 這個部分我們在後續的 shell
script 再次提及的!)

如果說的學理一點,那麼由於在 Linux System 下面,所有的執行續都是需要一個執行碼, 而就如同上面提到的,你『真正以 shell 來跟 Linux 溝通,是在正確的登入 Linux 之後!』這個時候你就有一個 bash 的執行程序,也才可以真正的經由 bash 來跟系統溝通囉!而在進入
shell 之前,也正如同上面提到的,由於系統需要一些變數來提供他資料的存取(或者是一些環境的設定參數值, 例如是否要顯示彩色等等的),所以就有一些所謂的『環境變數』 需要來讀入系統中了!這些環境變數例如 PATH、HOME、MAIL、SHELL 等等,都是很重要的,
為了區別與自訂變數的不同,環境變數通常以大寫字元來表示呢!

好了,那麼我們就簡單的來對『什麼是變數』作個簡單的定義好了: 『變數就是以一組文字或符號等,來取代一些設定或者是一串保留的資料!』, 例如:我設定了『myname』就是『VBird』,所以當你讀取 myname 這個變數的時候,系統自然就會知道!哈!那就是 VBird
啦!最簡單的例子可以取 PATH 來說明!如果你對於『相對路徑與絕對路徑』還有點印象的話, 那麼應該曉得『要下達正確的指令,應該需要指定路徑與檔名』才行!例如你的 ls 指令應該需要以『/bin/ls』來下達指令才對,那麼為何你在任意的路徑下都可以執行 ls
呢?而不需要指定路徑呢?這是因為系統已經預設了一些『搜尋路徑(PATH)』了, 所以當你需要執行一些指令的時候,系統就會依照該 PATH 的設定來進行指令的搜尋!而這個 PATH 就是所謂的變數了!

那麼如何『顯示變數』呢?這就需要使用到 echo 這個指令啦!

小標題的圖示變數的取用與設定:echo, 變數設定規則, unset
說的口沫橫飛的,也不知道『變數』與『變數代表的內容』有啥關係? 當然啦,那我們就將『變數』的『內容』拿出來給您瞧瞧就好了。利用 echo 這個指令來取用變數, 但是,變數在被取用時,前面必須要加上 $ 才行,舉例來說,要知道 PATH 的內容,該如何是好?

[root@linux ~]# echo $variable
[root@linux ~]# echo $PATH
/bin:/sbin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin
[root@linux ~]# echo ${PATH}

變數的取用就如同上面的範例,利用 ehco 就能夠讀出,只是需要在變數名稱前面加上 $ , 或者是以 ${variable} 的方式來取用都可以!當然啦,那個 echo 的功能可是很多的, 我們這裡單純是拿 echo 來讀出變數的內容而已,更多的 echo 使用,請自行給他 man echo 吧! ^_^

例題一:請在螢幕上面顯示出您的環境變數 HOME 與 MAIL:
答:

      echo $HOME
      echo $MAIL


OK!現在我們知道了變數與變數內的之間的相關性了,好了,那麼我要如何『設定』或者是『修改』 某個變數的內容啊?!很簡單啦!用『等號(=)』連接變數與他的內容就好啦!舉例來說: 我要將 myname 這個變數名稱的內容設定為 VBird ,那麼:

[root@linux ~]# echo $myname
   <==這裡並沒有任何資料~因為這個變數尚未被設定!是空的!
[root@linux ~]# myname=VBird
[root@linux ~]# echo $myname
VBird  <==出現了!因為這個變數已經被設定了!

瞧!如此一來,這個變數名稱 myname 的內容就帶有 VBird 這個資料囉~ 而由上面的例子當中,我們也可以知道: 當一個變數名稱尚未被設定時,預設的內容是『空』的。 另外,變數在設定時,還是需要符合某些規定的,否則會設定失敗喔! 這些規則如下所示啊!

   1. 變數與變數內容以等號『=』來連結;
   2. 等號兩邊不能直接接空白字元;
   3. 變數名稱只能是英文字母與數字,但是數字不能是開頭字元;
   4. 若有空白字元可以使用雙引號『 " 』或單引號『 ' 』來將變數內容結合起來,但須要特別留意, 雙引號內的特殊字元可以保有變數特性,但是單引號內的特殊字元則僅為一般字元;
   5. 必要時需要以跳脫字元『 \ 』來將特殊符號 ( 如 Enter, $, \, 空白字元, ' 等 ) 變成一般符號;
   6. 在一串指令中,還需要藉由其他的指令提供的資訊,可以使用 quote 『 ` command` 』;(特別特別注意,那個 ` 是鍵盤上方的數字鍵 1 左邊那個按鍵,而不是單引號!)
   7. 若該變數為擴增變數內容時,則需以雙引號及 $變數名稱 如:『 "$PATH":/home』繼續累加內容;
   8. 若該變數需要在其他子程序執行,則需要以 export 來使變數變成環境變數, 如『export PATH』;
   9. 通常大寫字元為系統預設變數,自行設定變數可以使用小寫字元,方便判斷 ( 純粹依照使用者興趣與嗜好 ) ;
  10. 取消變數的方法為:『unset 變數名稱』。

底下我們舉幾個例子來讓您試看看,就知道怎麼設定好您的變數囉!

範例一:設定一變數 name ,且內容為 VBird 。
[root@linux ~]# 12name=VBird
-bash: 12name=VBird: command not found  <==螢幕會顯示錯誤!因為不能以數字開頭!
[root@linux ~]# name = VBird  <==還是錯誤!因為有空白!
[root@linux ~]# name=VBird    <==OK 的啦!

範例二:承上題,若變數內容為 VBird's name 呢?
[root@linux ~]# name=VBird's name  
# 因為單引號可以將 Enter 這個特殊字符取消,所以,您可以繼續在下一行輸入內容~
# 不過,這與我們要達到的功能不同,所以,算是失敗的啦!
[root@linux ~]# name="VBird's name"  <==OK 的啦!
[root@linux ~]# name=VBird\'s\ name
# 利用反斜線 (\) 跳脫特殊字元,例如單引號與空白鍵,這也是 OK 的啦!

範例三:我要在 PATH 這個變數當中『累加』:/home/dmtsai/bin 這個目錄
[root@linux ~]# PATH=$PATH:/home/dmtsai/bin
[root@linux ~]# PATH="$PATH":/home/dmtsai/bin
# 上面這兩種格式在 PATH 裡頭的設定都是 OK 的!但是底下的例子就不見得囉!

範例四:呈範例三,我要將 name 的內容多出 "yes" 呢?
[root@linux ~]# name=$nameyes  
# 知道了吧?如果沒有雙引號,那麼變數成了啥?name 的內容是 $nameyes 這個變數!
# 呵呵!我們可沒有設定過 nameyes 這個變數吶!所以,應該是底下這樣才對!
[root@linux ~]# name="$name"yes
[root@linux ~]# name=${name}yes

範例五:如何讓我剛剛設定的 name=VBird 可以用在下個 shell 的程序?
[root@linux ~]# name=VBird
[root@linux ~]# bash        <==進入到所謂的子程序
[root@linux ~]# echo $name  <==嘿嘿!並沒有剛剛設定的內容喔!
[root@linux ~]# exit        <==離開剛剛的子程序
[root@linux ~]# export name
[root@linux ~]# bash        <==進入到所謂的子程序
[root@linux ~]# echo $name  <==出現了設定值了!
[root@linux ~]# exit        <==離開剛剛的子程序
# 什麼是『子程序』呢?就是說,在我目前這個 shell 的情況下,
# 去啟用另一個新的 shell ,新的那個 shell 就是子程序啦!在一般的狀態下,
# 父程序的自訂變數是無法在子程序內使用的。但是透過 export 將變數變成
# 環境變數後,就能夠在子程序底下應用了!很不賴吧!至於程序的相關概念,
# 我們會在『程序與資源管理』章節當中提到的喔!

範例六:如何進入到您目前核心的模組目錄?
[root@linux ~]# cd /lib/modules/`uname -r`/kernel
# 每個作業系統核心版本都不相同,以 FC4 為例,他的預設核心版本是
# 2.6.11-1.1369_FC4 所以,他的模組目錄在 /lib/modules/2.6.11-1.1369_FC4/kernel 。
# 因為每個 distributions 的這個值都不相同,但是我們卻可以利用 uname -r 這個指令
# 先取得版本資訊,所以囉,就可以透過上面指令當中的內含指令 `uname -r`
# 先取得版本輸出到 cd .. 那個指令當中,就能夠順利的進入目前核心的驅動程式所放置
# 的目錄囉!很方便吧!

範例七:取消剛剛設定的 name 這個變數內容
[root@linux ~]# unset name

根據上面的案例你可以試試看!就可以瞭解變數的設定囉!這個是很重要的呦!請勤加練習!! 其中,較為重要的一些特殊符號的使用囉!例如單引號、雙引號、跳脫字元、錢字號、quote 符號等等,底下的例題想一想吧!

例題二:在變數的設定當中,單引號與雙引號的用途有何不同?
答:

      單引號與雙引號的最大不同在於雙引號仍然可以保有變數的內容,但單引號內僅能是一般字元 ,而不會有特殊符號。我們以底下的例子做說明:假設您定義了一個變數, name=VBird ,現在想以 name 這個變數的內容定義出 myname 顯示 VBird its me 這個內容,要如何訂定呢?

            [root@linux ~]# name=VBird
            [root@linux ~]# echo $name
            VBird
            [root@linux ~]# myname="$name its me"
            [root@linux ~]# echo $myname
            VBird its me
            [root@linux ~]# myname='$name its me'
            [root@linux ~]# echo $myname
            $name its me
      發現了嗎?沒錯!使用了單引號的時候,那麼 $name 將失去原有的變數內容, 僅為一般字元的顯示型態而已!這裡必需要特別小心在意!


例題三:在指令下達的過程中, quote ( ` ) 這個符號代表的意義為何?
答:

      在一串指令中,在 ` 之內的指令將會被先執行,而其執行出來的結果將做為外部的輸入資訊!例如 uname -r 會顯示出目前的核心版本,而我們的核心版本在 /lib/modules 裡面,因此,你可以先執行 uname -r 找出核心版本,然後再以『 cd
目錄』到該目錄下,當然也可以執行如同上面範例六的執行內容囉。

      另外再舉個例子,我們也知道, locate 指令可以列出所有的相關檔案檔名,但是, 如果我想要知道各個檔案的權限呢?舉例來說,我想要知道每個 crontab 相關檔名的權限:

            [root@linux ~]# ls -l `locate crontab`
      如此一來,先以 locate 將檔名資料都列出來,再以 ls 指令來處理的意思啦!瞭了嗎? ^_^


小標題的圖示變數的用途
我們知道 PATH 這個變數是我們在執行指令的時候,所需要具備的指令搜尋目錄資料, 沒有他,我們就得要使用絕對路徑來下達指令才行。當然,還有很多變數都有他特別的意義存在。 除此之外,『我為何需要設定變數』呢?
要跟大家介紹這個『變數』,當然是因為他有相當程度的意義存在的啊! 底下就跟大家介紹一下,鳥哥設定變數的時機喔!

      我的案例一:最簡單的例子就是 『簡化路徑名稱』囉!以鳥哥為例,我的工作在 Unix 系統之下進行一些數值模式的模擬工作,偏偏由於資料量太大, 為了怕日後忘記這個目錄的內容與主要的意義,所以我的檔名都取的很長,
偏偏在執行模式的過程中,常常會切換目錄!我哩ㄌㄟ,光是打那幾行路徑名稱就快要瘋掉了! 所以我就設定那幾行目錄名稱成為一個四個字元的變數,如此一來我只要輸入『 cd $VARI 』這個指令,嘿嘿!馬上就移動到該路徑下了!很方便吧!當然變數的意義還不止於此,
不過這是最簡單的實例說明囉!

      我的案例二:另外一個常常需要變數的咚咚是在 scripts 裡面,例如我寫的一個偵測登錄檔的小程式 logfile.sh 這個咚咚, 由於裡頭常常需要用到『儲存路徑』,偏偏可能每個人的存取路徑都不太一樣,
而如果要修改存取路徑的話,嘿嘿!好幾十行要同時修改呢!還可能會改錯! 那麼我只要定義一個變數,然後後續的所有資料都使用這個變數的內容!嘿嘿! 那麼只要大家修改了這個變數的內容(只要一行),後續的動作就不需要修正了!這個動作常在程式或者是 script
當中看到的!

所以囉,有很多的時候為了方便或者是使用於 scripts 的意義,我們必須要設定變數! 當然囉,如果是跟系統終端機環境有關的設定值,很多也是利用變數來幫助達成的~ 底下我們就來談一談所謂的『環境變數』吧!

小標題的圖示環境變數的功能
環境變數可以幫我們達到很多功能~包括家目錄的變換啊、提示字元的顯示啊、執行檔搜尋的路徑啊等等的, 還有很多很多啦!那麼,既然環境變數有那麼多的功能,問一下,目前我的 shell 環境中, 有多少變數啊?!呵呵!我們可以利用兩個指令來查閱,分別是 env 與 export
呢!

# 一些環境變數的說明: env

範例一:列出目前的 shell 環境下的所有環境變數與其內容。
[root@linux ~]# env
HOSTNAME=linux.dmtsai.tw   <== 這部主機的主機名稱
SHELL=/bin/bash            <== 目前這個環境下,使用的 Shell 是哪一個程式?
TERM=xterm                 <== 這個終端機使用的環境是什麼類型
HISTSIZE=1000              <== 這個就是『記錄指令的筆數』在 FC4 預設可記錄 1000 筆
USER=root                  <== 使用者的名稱啊!
LS_COLORS=no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01:
or=01;05;37;41:mi=01;05;37;41:ex=00;32:*.cmd=00;32:*.exe=00;32:*.com=00;32:*.btm=0
0;32:*.bat=00;32:*.sh=00;32:*.csh=00;32:*.tar=00;31:*.tgz=00;31:*.arj=00;31:*.taz=
00;31:*.lzh=00;31:*.zip=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;31:*.bz=00;3
1:*.tz=00;31:*.rpm=00;31:*.cpio=00;31:*.jpg=00;35:*.gif=00;35:*.bmp=00;35:*.xbm=00
;35:*.xpm=00;35:*.png=00;35:*.tif=00;35: <== 一些顏色顯示
ENV=/root/.bashrc          <== 使用的個人環境設定檔
MAIL=/var/spool/mail/root  <== 這個使用者所取用的 mailbox 位置
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin:
/root/bin                  <== 不再多講啊!是執行檔指令搜尋路徑
INPUTRC=/etc/inputrc       <== 與鍵盤按鍵功能有關。可以設定特殊按鍵!
PWD=/root                  <== 目前使用者所在的工作目錄 (利用 pwd 取出!)
LANG=en_US.UTF-8           <== 這個與語系有關,底下會再介紹!
HOME=/root                 <== 這個使用者的家目錄啊!
_=/bin/env                 <== 上一次使用的指令的最後一個參數(或指令本身)

env 是 environment (環境) 的簡寫啊~ 上面的例子當中,是列出來所有的環境變數。當然,如果使用 export 也會是一樣的內容~ 只不過, export 還有其他額外的功能就是了,我們等一下再提這個 export 指令。
那麼上面這些變數有些什麼功用呢?底下我們就一個一個來分析分析!

    * HOME : 代表使用者的家目錄。還記得我們可以使用 cd ~ 去到使用者的家目錄嗎?或者利用 cd 就可以直接回到使用者家目錄了。那就是取用這個功能啦~ 有很多程式都可能會取用到這個變數的值喔!

    * SHELL : 告知我們,目前這個環境使用的 SHELL 是哪支程式? 如果是 bash 的話,預設是 /bin/bash 的啦!

    * HISTSIZE : 這個與『歷史命令』有關,亦即是, 我們曾經下達過的指令可以被系統記錄下來,而記錄的『筆數』則是由這個值來設定的。

    * ENV : 這個使用者所使用的個人化環境設定檔的讀取檔案。

    * MAIL : 當我們使用 mail 這個指令在收信時,系統會去讀取的郵件信箱檔案 (mailbox)。

    * PATH : 就是執行檔搜尋的路徑啦~目錄與目錄中間以冒號(分隔, 由於檔案的搜尋是依序由 PATH 的變數內的目錄來查詢,所以,目錄的順序也是重要的喔。

    * LANG : 這個重要!就是語系檔案囉~很多資料都會用到他, 舉例來說,當我們在啟動某些 perl 的程式語言檔案時,他會主動的去分析語系資料檔案, 如果發現有他無法解析的編碼語系,可能會產生錯誤喔!一般來說,我們中文編碼通常是 zh_TW.Big5 或者是
zh_TW.UTF-8,這兩個編碼偏偏不容易被解譯出來,所以,有的時候,可能需要修訂一下語系資料。 這部分我們會在下個小節做介紹的!

    * RANDOM : 這個玩意兒就是『隨機亂數』的變數啦!目前大多數的 distributions 都會有亂數產生器,那就是 /dev/random 這個檔案。 我們可以透過這個亂數檔案相關的變數 ($RANDOM) 來隨機取得亂數值喔。在 BASH 的環境下,這個 RANDOM 變數的內容,介於 0~32767
之間,所以,你只要 echo $RANDOM 時,系統就會主動的隨機取出一個介於 0~32767 的數值。萬一我想要使用 0~9 之間的數值呢?呵呵~利用 declare 宣告數值類型, 然後這樣做就可以了:

      [root@linux ~]# declare -i number=$RANDOM*10/32767 ; echo $number
      8   <== 此時會隨機取出 0~9 之間的數值喔!

大致上是有這些環境變數啦~裡面有些比較重要的參數,在底下我們都會另外進行一些說明的~
# 其他所有的變數說明: set
而除了這些環境變數之外,還有沒有什麼重要的變數呢?當然有啊! 我們在 bash 的環境下,其實還有一些挺重要的變數,這些變數是『在這個 shell 環境下有效』的, 如果是在『子程序』,這些變數值就不會相同了。 那麼如何觀察目前 shell
環境下的所有變數呢?很簡單啊,就用 set 即可!set 這個指令除了會將環境變數列出來之外,其他我們的自訂變數,與所有的變數,都會被列出來喔!資訊多好多。 底下僅列出幾個重要的內容。

[root@linux ~]# set
BASH=/bin/bash           <== bash 的主程式放置路徑
BASH_VERSINFO=([0]="3" [1]="00" [2]="16" [3]="1" [4]="release"
[5]="i386-redhat-linux-gnu")      <== bash 的版本啊!
BASH_VERSION='3.00.16(1)-release' <== bash 的版本啊!
COLORS=/etc/DIR_COLORS.xterm      <== 使用的顏色紀錄檔案
COLUMNS=115              <== 在目前的終端機環境下,使用的欄位有幾個字元長度
HISTFILE=/root/.bash_history      <== 歷史命令記錄的放置檔案,隱藏檔
HISTFILESIZE=1000        <== 存起來(與上個變數有關)的檔案之指令的最大紀錄筆數。
HISTSIZE=1000            <== 目前環境下,可記錄的歷史命令最大筆數。
HOSTTYPE=i386            <== 主機安裝的軟體主要類型。我們用的是 i386 相容機器軟體
IFS=$' \t\n'             <== 預設的分隔符號
LINES=35                 <== 目前的終端機下的最大行數
MACHTYPE=i386-redhat-linux-gnu    <== 安裝的機器類型
MAILCHECK=60             <== 與郵件有關。每 60 秒去掃瞄一次信箱有無新信!
OLDPWD=/home             <== 上個工作目錄。我們可以用 cd - 來取用這個變數。
OSTYPE=linux-gnu         <== 作業系統的類型!
PPID=20046               <== 父程序的 PID (會在後續章節才介紹)
PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME%%.*}:${PWD/#$HOME/~}\007"'
                         <== 上面這個是命令提示字元!與底下也有關。
PS1='[\u@\h \W]\$ '      <== PS1 就厲害了。這個是命令提示字元,也就是我們常見的
                             [root@linux ~]# 或 [dmtsai ~]$ 的設定值啦!可以更動的!
RANDOM=13586             <== 亂數啊!上面已經提過囉~
SUPPORTED=zh_TW.UTF-8:zh_TW:zh:en_US.UTF-8 <== 本系統所支援的語系
name=VBird               <== 剛剛設定的自訂變數也可以被列出來喔!
$                        <== 目前這個 shell 所使用的 PID
?                        <== 剛剛執行完指令的回傳值。

一般來說,不論是否為環境變數,只要跟我們目前這個 shell 的操作介面有關的變數, 通常都會被設定為大寫字元,也就是說,『基本上,在 Linux 預設的情況中,使用{大寫的字母}來設定的變數一般為系統內定需要的變數』。

使用 set 除了會將系統的預設值秀出來之外,連帶的所有的你自己設定的變數也會被秀出來! 同時需要注意的是,若當時有相當多人同時在線上的話,那麼 你的變數只能給自己使用 ( 除非改的是系統的預設參數檔,如 /etc/profile ),而不會干擾到別人的!就如同前面所說的,
由於你登入 Linux 之後會取得一個 PID ,而你的設定將只對這個 PID 與子程序有關!此外, 這次登入所進行的變數設定,如果沒有更動到設定檔, 那麼這次設定的變數在下次登入時將被取消掉 ( 因為程序 PID 不見囉! ) !所以囉,
如果你想要你的變數每次都能在你登入的時候自動就設定好了,那麼就必須將你的設定寫入登入時載入的設定檔! ( 更多的程序相關的說明,不要急~我們會在後面的 程序與資源管理 當中好好的提一提的! )

OK!OK!那麼上頭那些變數當中,有哪些是比較重要的?大概有這幾個吧!

    * PS1:(提示字元的設定)

      這是 PS1 (數字的 1 不是英文字母!),這個東西就是我們的『命令提示字元』啊! 當我們每次按下 [Enter] 按鍵去執行某個指令後,最後要再次出現提示字元時, 就會主動去讀取這個變數值了。上頭 PS1 內顯示的是一些特殊符號,每個版本 bash 的 PSI
變數內的特殊符號可能有些許的差異, 你應該主動的以 man bash 去查詢一下相關的變數。底下我列出 FC4 的環境下, 預設的 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 比較有趣,
      # 如果您按下 [Enter] 後,該數字就會增加喔!為啥?上面有說明ㄇㄟ!


    * $:(關於本 shell 的 PID)

      其實這個咚咚代表的是『目前這個 Shell 的執行緒代號』,亦即是所謂的 PID (Process ID)。 更多的程序觀念,我們會在第四章的時候提及。想要知道我們的 shell 的 PID ,就可以: echo $$ 即可!

    * ?:(關於上個執行指令的回傳碼)

      蝦密?問號也是一個特殊的變數?沒錯!在 bash 裡面這個變數可重要的很! 這個變數是:『上個執行的指令所回傳的值』, 上面這句話的重點是『上一個指令』與『回傳值』兩個地方。當我們執行某些指令時,
這些指令都會回傳一個執行後的代碼。一般來說,如果成功的執行該指令, 則會回傳一個 0 值,如果執行過程發生錯誤,就會回傳『錯誤代碼』才對!一般就是以非為 0 的數值來取代。 我們以底下的例子來看看:

      [root@linux ~]# echo $SHELL
      /bin/bash
      [root@linux ~]# echo $?
      0
      # 因為上個指令執行過程中,並沒有錯誤,為成功的執行完畢,所以回傳 0 。
      [root@linux ~]# 12name=VBird
      -bash: 12name=VBird: command not found
      [root@linux ~]# echo $?
      127
      # 發生錯誤啦!所以 echo $? 時,就會出現錯誤的代碼!
      # 我們可以利用這個代碼來搜尋錯誤的原因喔!
      [root@linux ~]# echo $?
      0
      # 咦!怎麼又變成正確了?這是因為 "?" 只與『上一個執行指令』有關,
      # 所以,我們上一個指令是執行『 echo $? 』,當然沒有錯誤,所以是 0 沒錯!


    * OSTYPE, HOSTTYPE, MACHTYPE:(主機硬體與核心的等級)

      這幾個東西與程式的安裝有關。我們在『Linux 主機規劃』 裡面提到過關於主機的等級方面的問題,當我們在安裝軟體的時候, 需要透過編譯器來將原始碼編譯成為二進位的檔案 (binary file)。但是,
我們可以針對硬體的配備來進行編譯的最佳化,此時,這些參數就可以被用到了! 基本上,目前主要的 distribution 都是針對 i386 亦即最低等級的機器進行最佳化, 這樣才能夠安裝在較高階的機器上,如果以 686 的機型來最佳化, 那麼,可就無法向下相容的喔!(早期的
OpenLinux 是針對 686 機器來釋出軟體, 所以,當時的 OpenLinux 是無法安裝在 P-166 的機器上的。 )

自訂變數轉成環境變數: export
好了,上面我們環境變數也提過了,一些自訂變數也提過了,那麼,這兩者有啥不同? 他的不同處,我們在 變數設定規則 當中稍微提過, 主要是由於變數可否被子程序所引用。

當你取得一個 bash 之後,亦即得到了一個程序了,但是若你再次的執行一次 bash ,那麼你將進入『子程序』,這個程序的概念我們在資源管理章節中再詳談,這裡您先有個概念即可。 那麼由於您已經進入了該子程序,所以在父程序中的自訂變數設定將不再繼續的存在。
會存在子程序中的,僅有『環境變數』。

換個角度來想,也就是說,如果我能將自訂變數變成環境變數的話,那不就可以讓該變數值繼續存在於子程序了? 呵呵!沒錯!此時,那個 export 指令就很有用啦! 如您想要讓該變數內容繼續的在子程序中使用,那麼就請執行:

      export 變數

這個東西用在『引用他人的檔案或者其他程序』時,相當的重要的! 尤其像鳥哥常常兩三個檔案互相引用來引用去的,如果忘記設定 export 的話,那麼不同的檔案中的相同變數值,將需要一再地重複設定才行!所以,我只要在頭一個檔案使用 export
的話,那麼後續的檔案引用時,將會把該變數內容讀進來!好用的很,如果僅下達 export 而沒有接變數時,那麼此時將會把所有的『環境變數』秀出來喔!例如:

[root@linux ~]# export
declare -x ENV="/root/.bashrc"
declare -x HISTSIZE="1000"
declare -x HOME="/root"
declare -x HOSTNAME="linux.dmtsai.tw"
declare -x INPUTRC="/etc/inputrc"
declare -x LANG="en_US.UTF-8"
declare -x MAIL="/var/spool/mail/root"
declare -x SHELL="/bin/bash"
# 很多都直接省略了!不然....重複性太高,浪費版面~ ^_^

小標題的圖示語系檔案的變數 (locale)
還記得我們在首次進入 Linux 那個章節裡面提到的,關於語系編碼的問題嗎? 就是當我們使用 man command 的方式去查詢某個資料的說明檔時,該說明檔的內容可能會因為我們使用的語系, 而產生一些亂碼。另外,利用 ls 查詢檔案的時間時,也可能會有亂碼出現在時間的部分。
那個問題其實就是語系的問題啦。

目前大多數的 Linux distributions 已經都是支援萬國碼,此外,也都支援大部分的語言語系了。 這有賴於 i18n 支援的幫助呢! 那麼我們的 Linux 到底支援了多少的語系呢?這可以由 locale 這個指令來查詢到喔!

[root@linux ~]# locale -a
aa_DJ
aa_DJ.iso88591
en_US
en_US.iso88591
en_US.iso885915
en_US.utf8
zh_TW
zh_TW.big5
zh_TW.euctw
zh_TW.utf8
# 其實輸出的內容有很多,鳥哥將一些資訊捨棄了~
# 從上面的輸出中,我們也不難看出,系統是有支援 big5, utf8 等中文語系資料的!

中文語系至少支援了兩種以上的編碼,一種是目前還是很常見的 big5 ,另一種則是越來越熱門的 utf-8 編碼。 那麼我們如何修訂這些編碼呢?其實可以透過底下這些變數的說:

[root@linux ~]# LANG         <==主語言的環境
[root@linux ~]# LC_CTYPE     <==字元辨識的編碼
[root@linux ~]# LC_NUMERIC   <==數字系統的顯示訊息
[root@linux ~]# LC_TIME      <==時間系統的顯示資料
[root@linux ~]# LC_COLLATE   <==字串的比較與排序等
[root@linux ~]# LC_MONETARY  <==幣值格式的顯示等
[root@linux ~]# LC_MESSAGES  <==訊息顯示的內容,如功能表、錯誤訊息等
[root@linux ~]# LC_ALL       <==語言環境的整體設定。

基本上,你可以逐一設定每個與語系有關的變數資料,但事實上,如果其他的語系變數都未設定, 且您有設定 LANG 或者是 LC_ALL 時,則其他的語系變數就會被這兩個變數所取代! 這也是為什麼我們在 FC4 當中,通常僅設定 LANG 這個變數而已!因為他是最主要的設定變數。
好了,那麼你應該要覺得奇怪的是,為什麼在 Linux 主機的終端機介面 (tty1 ~ tty6) 的環境下,如果 LANG=zh_TW.big5 這個設定值生效後,使用 man 或者其他訊息輸出時, 都會有一堆亂碼,尤其是使用 ls -l 這個參數時?

因為在 Linux 主機的終端機介面下,那個環境是無法顯示像中文這麼複雜的編碼的文字, 所以,就會產生亂碼了。也就是如此,所以,我們才會必須要在 tty1 ~ tty6 的環境下, 加裝一些中文化介面的軟體,才能夠看到中文啊!不過,如果您是在 Windows
主機以遠端連線伺服器的軟體連線到主機的話,那麼,嘿嘿!其實文字介面確實是可以看到中文的。 所以,此時反而您得要在 LANG 設定中文編碼才好呢!

無論如何,如果發生一些亂碼的問題,那麼設定系統裡面保有的語系編碼, 例如: en_US 或 en_US.utf8 等等的設定,應該就 OK 的啦!好了,那麼系統預設支援多少種語系呢? 當我們使用 locale 時,系統是列出目前 Linux 主機內保有的語系檔案, 這些語系檔案都放置在:
/usr/lib/locale/ 這個目錄中。 但是,目前的這個 shell 環境所支援的語系,則是要看 SUPPORTED 這個變數才對喔!

那麼,如果我想要修訂系統的語系支援呢?可以修訂 /etc/sysconfig/i18n 這個檔案呢! 這個檔案的內容有點像這樣:

[root@linux ~]# vi /etc/sysconfig/i18n
LANG="en_US.UTF-8"
SYSFONT="latarcyrheb-sun16"
SUPPORTED="zh_TW.UTF-8:zh_TW:zh:en_US.UTF-8"

你可以在這個檔案當中加入 LC_TIME 或者其他語系相關變數的設定內容, 也可以直接修改 LANG 那個變數即可啊! ^_^ 但,事實上,我們還可以透過個人的環境設定檔來設定 LANG 呢! 如此一來,則不必修訂系統的語系檔案,比較安全啦!
Tips:
假設你用 vi 編輯一個純文字檔,這個純文字檔在編輯的時候,是在 Windows 上面編輯的, 那麼這個檔案的預設編碼應該是以 zh_TW.big5 所編輯的才對。但是,如果你的 shell 環境中, 卻是使用 LANG=en_US 時,則當你編輯該檔案時,就可能會看到『亂碼』,
或者輸入的中文可能會變成『亂碼』了。此時,只要你離開 vi ,然後執行 LANG=zh_TW.big5 , 然後再重新以 vi 編輯該檔案,呵呵!應該就能夠看到中文啦!但是請注意, 這個方法當然不適用 tty1 ~ tty6 的環境,原因上面已經提過囉~ 僅適合以類似 putty 軟體由 Windows
電腦連線到 linux 主機上的做業!         
小標題的圖示變數的有效範圍
蝦密??變數也有使用的『範圍』?沒錯啊~我們在上頭的 export 指令說明中,就提到了這個概念了。如果在跑程式的時候,有父程序與子程序的不同程序關係時, 則『變數』可否被引用是 export 有關。被 export 後的變數,我們可以稱他為『環境變數』!
環境變數可以被子程序所引用,但是其他的自訂變數內容就不會存在於子程序中。也就是說: 我們自行設定的變數,只在目前這個 shell 環境當中存在, 在子程序中將不會存在此一變數。除非使用 export 將自訂變數變成環境變數。

其實除了 shell 的父、子程序外,在腳本( scripts )的編寫當中,由於有的軟體會使用到 2 個以上的 scripts 做為一個完整的套件!也就是說,假如你有兩支程式,一支為 scripts1.sh 以及 scripts2.sh ,而 scripts2.sh 會去引用 scripts1.sh 的變數,這個時候,嘿嘿!你在
scripts1.sh 當中設定的變數請『千萬記得以 export 設定』, 否則你的變數將無法在兩個 scripts 之間互相被引用喔!當這個 scripts 執行完畢之後,剛剛在 scripts 當中設定的變數也就『失效了!』。

其實,要瞭解不同程序之間變數的變換,應該要先瞭解『程序』的概念比較好, 但是我們還沒有講到.....沒關係~等你念到程序章節後,還可以再回來好好的看一看。 基本上,環境變數可以讓子程序繼續引用的原因,是因為:

    * 當啟動一個 shell ,作業系統分配一記憶區塊給 shell 使用,此區域之變數可以讓子程序存取;

    * 利用 export 功能,可以讓變數的內容寫到上述的記憶區塊當中(環境變數);
    * 當載入另一個 shell 時 (亦即啟動子程序,而離開原本的父程序了),子 shell 可以將父 shell 的環境變數所在的記憶區塊導入自己的環境變數區塊當中。

透過這樣的關係,我們就可以讓某些變數可以在相關的程序之間存在,以幫助自己更方便的操作環境喔!
小標題的圖示變數鍵盤讀取、陣列與宣告: read, array, declare
我們上面提到的變數設定功能,都是直接由指令列直接設定的,那麼,可不可以讓使用者能夠經由鍵盤輸入? 什麼意思呢?是否記得某些程式執行的過程當中,會等待使用者輸入 "yes/no" 之類的訊息啊!? 在 bash 裡面也有相對應的功能喔!此外,我們還可以宣告這個變數的屬性,
例如:陣列或者是數字等等的。底下就來看看吧!

# 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
,嘿嘿!後面就會有比較多可以用的提示字元給我們參考! 在指令的下達裡面,比較美觀啦! ^_^

# 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 比較常用的啦!

# 陣列屬性 array 說明
某些時候,我們必須使用陣列來宣告一些變數,這有什麼好處啊?在一般人的使用上, 果然是看不出來有什麼好處的!不過,如果您曾經寫過程式的話,那才會比較瞭解陣列的意義~ 陣列對寫數值程式的設計師來說,可是不能錯過學習的重點之一哩!好!不囉唆~
那麼要如何設定陣列的變數與內容呢?在 bash 裡頭,陣列的設定方式是:

      var[index]=content

意思是說,我有一個陣列名稱為 var ,而這個陣列的內容為 var[1]=小明, var[2]=大明, var[3]=好明 .... 等等,那個 index 就是一些數字啦,重點是用中刮號 ([ ]) 來設定的。 目前我們 bash 提供的是一維陣列。老實說,如果您不必寫一些複雜的程式,
那麼這個陣列的地方,可以先略過,等到有需要再來學習即可!因為要製作出陣列, 通常與迴圈或者其他判斷式交互使用才有比較高的意義存在!

範例:設定上面提到的 var[1] ~ var[3] 的變數。
[root@linux ~]# var[1]="small min"
[root@linux ~]# var[2]="big min"
[root@linux ~]# var[3]="nice min"
[root@linux ~]# echo "${var[1]}, ${var[2]}, ${var[3]}"

比較有趣的地方在於『讀取』,一般來說,建議直接以 ${陣列} 的方式來讀取, 比較正確無誤的啦!
小標題的圖示與檔案系統及程序的限制關係: ulimit
想像一個狀況:我的 Linux 主機裡面同時登入了十個人,這十個人不知怎麼搞的, 同時開啟了 100 個檔案,每個檔案的大小約 10MBytes ,請問一下, 我的 Linux 主機的記憶體要有多大才夠? 10*100*10 = 10000 MBytes ~~
老天爺,這樣,系統不掛點才有鬼哩!為了要預防這個情況的發生,所以, 我們的 bash 是可以『限制使用者的某些系統資源』的,包括可以開啟的檔案數量, 可以使用的 CPU 時間,可以使用的記憶體總量等等。如何設定?用 ulimit 吧!

[root@linux ~]# ulimit [-SHacdflmnpstuv] [配額]
參數:
-H  :hard limit ,嚴格的設定,必定不能超過設定的值;
-S  :soft limit ,警告的設定,可以超過這個設定值,但是會有警告訊息,
      並且,還是無法超過 hard limit 的喔!也就是說,假設我的 soft limit
      為 80 , hard limit 為 100 ,那麼我的某個資源可以用到 90 ,
      可以超過 80 ,還是無法超過 100 ,而且在 80~90 之間,會有警告訊息的意思。
-a  :列出所有的限制額度;
-c  :可建立的最大核心檔案容量 (core files)
-d  :程序資料可使用的最大容量
-f  :此 shell 可以建立的最大檔案容量 (一般可能設定為 2GB)單位為 Kbytes
-l  :可用於鎖定 (lock) 的記憶體量
-p  :可用以管線處理 (pipe) 的數量
-t  :可使用的最大 CPU 時間 (單位為秒)
-u  :單一使用者可以使用的最大程序(process)數量。
範例:
範例一:列出所有的限制資料
[root@linux ~]# ulimit -a

範例二:限制使用者僅能建立 1MBytes 以下的容量的檔案
[root@linux ~]# ulimit -f 1024

還記得我們在 Linux 磁碟檔案系統 裡面提到過,單一 filesystem 能夠支援的單一檔案大小與 block 的大小有關。例如 block size 為 1024 byte 時,單一檔案可達 16GB 的容量。但是,我們可以用 ulimit 來限制使用者可以建立的檔案大小喔! 利用 ulimit -f
就可以來設定了!例如上面的範例二,要注意單位喔!單位是 Kbytes。 若改天你一直無法建立一個大容量的檔案,記得瞧一瞧 ulimit 的資訊喔!( 不過,要注意的是,一般身份使用者如果以 ulimit 設定了 -f 的檔案大小, 那麼他『只能減小檔案大小,不能增加檔案大小喔!』)
小標題的圖示額外的變數設定功能
剛剛我們提到了兩種變數取用的方法,分別是這樣:

[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=${strexpr}        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 之類的~ 所以,您還是得要瞭解變數這個玩意才行喔! ^_^

大標題的圖示命令別名與歷史命令:
我們知道在早期的 DOS 年代,清除螢幕上的資訊可以使用 cls 來清除,但是在 Linux 裡面, 我們則是使用 clear 來清除畫面的。那麼可否讓 cls 等於 clear 呢?可以啊!用啥方法? link file 還是什麼的?別急!底下我們介紹不用 link file
的命令別名來達成。那麼什麼又是歷史命令? 曾經做過的舉動我們可以將他記錄下來喔!那就是歷史命令囉~底下分別來談一談這兩個玩意兒。

小標題的圖示命令別名設定: alias, unalias
命令別名是一個很有趣的東西,特別是你的慣用指令特別長的時候!還有, 增設預設的屬性在一些慣用的指令上面,可以預防一些不小心誤殺檔案的情況發生的時候! 舉個例子來說,如果你要查詢隱藏檔,並且需要長的列出與一頁一頁翻看,那麼需要下達『 ls -al | more
』這個指令,我是覺得很煩啦! 要輸入好幾個單字!那可不可以使用 lm 來簡化呢?!當然可以,你可以在命令列下面下達:

[root@linux ~]# alias lm='ls -l | more'

嘿嘿!我立刻多出了一個可以執行的指令喔!這個指令名稱為 lm ,且其實他是執行 ls -al | more 啊!真是方便。不過, 要注意的是:『alias 的定義規則與變數定義規則幾乎相同』, 所以你只要在 alias 後面加上你的 {『別名』='指令 參數' }, 以後你只要輸入 lm
就相當於輸入了 ls -al|more 這一串指令!很方便吧!

另外,命令別名的設定還可以取代既有的指令喔!舉例來說,我們知道 root 可以移除( rm )任何資料!所以當你以 root 的身份在進行工作時,需要特別小心, 但是總有失手的時候,那麼 rm 提供了一個參數來讓我們確認是否要移除該檔案,那就是 -i
這個參數!所以,你可以這樣做:

[root@linux ~]# alias rm='rm -i'

嘿嘿!那麼以後使用 rm 的時候,就不用太擔心會有錯誤刪除的情況了!這也是命令別名的優點囉! 那麼如何知道目前有哪些的命令別名呢?就使用 alias 呀!

[root@linux ~]# alias
alias l.='ls -d .* --color=tty'
alias ll='ls -l --color=tty'
alias lm='ls -al | more'
alias ls='ls --color=tty'
alias vi='vim'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'

由上面的資料當中,您也會發現一件事情啊,我們在 vi 文書編輯器 裡面提到 vi 與 vim 是不太一樣的, vi 是比較老,而 vim 可以用來取代 vi 喔。我們的 FC4 明明就同時有 vi/vim , 為何我執行 vi 會是進入 vim 呢?呵呵!那就是因為上面的表格當中的『 alias vi='vim'
』這個設定啦! 至於如果要取消命令別名的話,那麼就使用 unalias 吧!例如要將剛剛的 lm 命令別名拿掉,就使用:

[root@linux ~]# unalias lm

那麼命令別名與變數有什麼不同呢?基本上,他們的意義就不太一樣了! alias 這種命令別名,你可以將他想成是建立一個新的指令名稱, 至於變數則僅是將一個數值或者字串存在某個代表意義當中!舉個例子好了,我們知道以前的 DOS 年代,列出目錄與檔案就是 dir
,而清除螢幕就是 cls ,那麼如果我想要在 linux 裡面也使用相同的指令呢?那就以 alias 來進行指令的別名設定:

      alias cls='clear'
      alias dir='ls -l'

  只要加入這兩行,以後你輸入 cls 及 dir 就可以執行了!很方便吧!

小標題的圖示歷史命令:history
前面我們提過 bash 有提供指令歷史的服務!那麼如何查詢我們曾經下達過的指令呢?就使用 history 囉!當然,如果覺得 histsory 要輸入的字元太多太麻煩,可以使用命令別名來設定呢! 不要跟我說還不會設定呦! ^_^

      alias h='history'

如此則輸入 h 等於輸入 history 囉!好了,我們來談一談 history 的用法吧!

[root@linux ~]# history [n]
[root@linux ~]# history [-c]
[root@linux ~]# history [-raw] histfiles
參數:
n   :數字,意思是『要列出最近的 n 筆命令列表』的意思!
-c  :將目前的 shell 中的所有 history 內容全部消除
-a  :將目前新增的 history 指令新增入 histfiles 中,若沒有加 histfiles ,
      則預設寫入 ~/.bash_history
-r  :將 histfiles 的內容讀到目前這個 shell 的 history 記憶中;
-w  :將目前的 history 記憶內容寫入 histfiles 中!
範例:
範例一:列出目前記憶體內的所有 history 記憶
[root@linux ~]# history
# 前面省略
1017  man bash
1018  ll
1019  history
1020  history
# 列出的資訊當中,共分兩欄,第一欄為該指令在這個 shell 當中的代碼,
# 另一個則是指令本身的內容喔!至於會秀出幾筆指令記錄,則與 HISTSIZE 有關!

範例二:列出目前最近的 3 筆資料
[root@linux ~]# history 3
1019  history
1020  history
1021  history 3

範例三:立刻將目前的資料寫入 histfile 當中
[root@linux ~]# history -w
# 在預設的情況下,會將歷史紀錄寫入 ~/.bash_history 當中!
[root@linux ~]# echo $HISTSIZE
1000

在正常的情況下,當我們以 bash 登入 Linux 主機之後,系統會主動的由家目錄的 ~/.bash_history 讀取以前曾經下過的指令,那麼 ~/.bash_history 會記錄幾筆資料呢?這就與你 bash 的 HISTSIZE 這個變數設定值有關了!在預設的 FC4 底下,是會記錄 1000 筆資料的!
那麼假設我這次登入主機後,共下達過 100 次指令,『等我登出時, 系統就會將 101~1100 這總共 1000 筆歷史命令更新到 ~/.bash_history 當中。』 也就是說,歷史命令在我登出時,會將最近的 HISTSIZE 筆記錄到我的紀錄檔當中啦! 當然,也可以用 history -w
強制立刻寫入的!那為何用『更新』兩個字呢? 因為 ~/.bash_history 記錄的筆數永遠都是 HISTSIZE 那麼多,舊的訊息會被主動的拿掉! 僅保留最新的!

那麼 history 這個歷史命令只可以讓我查詢命令而已嗎?呵呵!當然不止啊! 我們可以利用相關的功能來幫我們執行命令呢!舉例來說囉:

[root@linux ~]# !number
[root@linux ~]# !command
[root@linux ~]# !!
參數:
number  :執行第幾筆指令的意思;
command :由最近的指令向前搜尋『指令串開頭為 command』的那個指令,並執行;
!!      :就是執行上一個指令(相當於按↑按鍵後,按 Enter)
範例:
[root@linux ~]# history
   66  man rm
   67  alias
   68  man history
   69  history
[root@linux ~]# !66  <==執行第 66 筆指令
[root@linux ~]# !!   <==執行上一個指令,本例中亦即 !66
[root@linux ~]# !al  <==執行最近以 al 為開頭的指令(上頭列出的第 67 個)

經過上面的介紹,瞭乎?歷史命令用法可多了!如果我想要執行上一個指令, 除了使用上下鍵之外,我可以直接以『 !! 』 來下達上個指令的內容,此外, 我也可以直接選擇下達第 n 個指令,『 !n 』來執行,也可以使用指令標頭,例如 『 !vi 』來執行最近指令開頭是 vi
的指令列!相當的方便而好用!基本上 history 的用途很大的!但是需要小心安全的問題!尤其是 root 的歷史紀錄檔案,這是 Cracker 的最愛!因為不小心的 root 會將很多的重要資料在執行的過程中會被紀錄在 ~/.bash_history
當中,如果這個檔案被解析的話,後果不堪吶!無論如何,使用 history 配合『 ! 』曾經使用過的指令下達是很有效率的一個指令方法!
大標題的圖示Bash Shell 使用環境:
是否記得我們登入主機的時候,螢幕上頭會有一些說明文字,告知我們的 Linux 版本啊什麼的, 還有,登入的時候,我們還可以給予使用者一些訊息或者歡迎文字呢。此外, 我們習慣的環境變數、命令別名等等的,是否可以登入就主動的幫我設定好?
這些都是需要來注意的。另外,這些設定值又可以分為系統整體設定值與各人喜好設定值, 僅是一些檔案放置的地點不同啦!這我們後面也會來談一談的!

小標題的圖示絕對路徑與相對路徑
這個議題說到快要爛掉了~從一開始到現在,這個絕對路徑與相對路徑的問題我們就提到不知道多少次了, 因為他實在很重要~這與 PATH 這個變數關係很大!老實說, 萬一你的 PATH 沒有設定完整的時候,下達指令就必須要以『 一長列的指令連帶根目錄都要列出來
』,呵呵那就是絕對路徑的設定法啦! 基本上,這個『 絕對路徑』與『相對路徑 』的觀念是很重要的!否則你將常常會找不到檔案說! 所謂的『絕對路徑』就是以根目錄開始寫入到檔案的一種命令寫定方法,舉例來說,我目前在 /home/test 這個 test
使用者的家目錄中,我想要看看裡面的 .bashrc 這個檔案的資料,使用的是 more 這個指令,而這個指令在 /bin/more 當中,則正確的下達指令的方法為:

[root@linux ~]# /bin/more .bashrc

我在的目錄為 /home/test !這是絕對路徑寫法! 而如果你還記得我們在 Linux 檔案與目錄管理 那一篇文章中提到的觀念的話,那麼應該記得使用 ls -al 時會出現兩個一定存在的目錄,分別是『.』與『..』,分別代表是『這個路徑』,與『上一層路徑』!

[root@linux ~]# ls -al
drwxrwxr-x  2 root  root   4096  Aug 15 11:05 .
drwxrwxr-x  2 root  root   4096  Aug 14 23:26 ..

所以說,要執行上一層目錄中的命令,可以下達『../command 』那個 command 指的是存在的可執行檔!那麼我因為在 /home/test 裡面,距離 /bin 有兩層上層目錄,所以我要使用 /bin/more 這個執行檔,並且使用相對路徑的方法,就必須使用:

[root@linux ~]# ../../b
发表于 2006-10-21 00:43:03 | 显示全部楼层
3n  要简体的
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2025-2-11 19:14 , Processed in 0.044790 second(s), 15 queries .

© 2001-2025 Discuz! Team. Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表