Linux系列6:bash學習

bash學習

1 認識 BASH 這個 Shell

管理整個計算機硬件的其實是操作系統的核心 (kernel),這個核心是需要被保護的! 所以我們一般使用者就只能透過 shell 來跟核心溝通,以讓核心達到我們所想要達到的工作。 那麼系統有多少 shell 可用呢?爲什麼我們要使用 bash 啊?底下分別來談一談喔!

1.1 硬件、核心與 Shell

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

  1. 硬件:當然就是需要你的硬件有『聲卡芯片』這個配備,否則怎麼會有聲音;
  2. 核心管理:操作系統的核心可以支持這個芯片組,當然還需要提供芯片的驅動程序囉;
  3. 應用程序:需要使用者 (就是你) 輸入發生聲音的命令囉!

這就是基本的一個輸出聲音所需要的步驟!也就是說,你必須要『輸入』一個命令之後, 『硬件』纔會透過你下達的命令來工作!那麼硬件如何知道你下達的命令呢?那就是 kernel (核心) 的控制工作了!也就是說,我們必須要透過『 Shell 』將我們輸入的命令與 Kernel 溝通,好讓 Kernel 可以控制硬件來正確無誤的工作! 基本上,我們可以透過底下這張圖來說明一下:
在這裏插入圖片描述
我們在第零章內的操作系統小節曾經提到過, 操作系統其實是一組軟件,由於這組軟件在控制整個硬件與管理系統的活動監測, 如果這組軟件能被用戶隨意的操作,若使用者應用不當,將會使得整個系統崩潰!因爲操作系統管理的就是整個硬件功能嘛! 所以當然不能夠隨便被一些沒有管理能力的終端用戶隨意使用囉!

但是我們總是需要讓用戶操作系統的,所以就有了在操作系統上面發展的應用程序啦!用戶可以透過應用程序來指揮核心, 讓核心達成我們所需要的硬件任務!如果考慮如第零章所提供的操作系統圖標(圖4.2.1), 我們可以發現應用程序其實是在最外層,就如同雞蛋的外殼一樣,因此這個咚咚也就被稱呼爲殼程序 (shell) 囉!

其實殼程序的功能只是提供用戶操作系統的一個接口,因此這個殼程序需要可以呼叫其他軟件纔好。 我們在第五章到第十章提到過很多命令,包括 man, chmod, chown, vi, fdisk, mkfs 等等命令,這些命令都是獨立的應用程序, 但是我們可以透過殼程序 (就是命令列模式) 來操作這些應用程序,讓這些應用程序呼叫核心來運行所需的工作哩! 這樣對於殼程序是否有了一定的概念了?

Tips:也就是說,只要能夠操作應用程序的接口都能夠稱爲殼程序。狹義的殼程序指的是命令列方面的軟件,包括本章要介紹的 bash 等。 廣義的殼程序則包括圖形接口的軟件!因爲圖形接口其實也能夠操作各種應用程序來呼叫核心工作啊! 不過在本章中,我們主要還是在使用 bash 啦!

1.2 爲何要學文字接口的 shell

文字接口的 shell 是很不好學的,但是學了之後好處多多!所以, 在這裏鳥哥要先對您進行一些心理建設,先來了解一下爲啥學習 shell 是有好處的,這樣你纔會有信心繼續玩下去 _

1.2.1 文字接口的 shell:大家都一樣!

鳥哥常常聽到這個問題:『我幹嘛要學習 shell 呢? 不是已經有很多的工具可以提供我配置我的主機了?我爲何要花這麼多時間去學命令呢?不是以 X Window 按一按幾個按鈕就可以搞定了嗎?』唉~還是得一再地強調, X Window 還有 Web 接口的配置工具例如 Webmin (注1) 是真的好用的傢伙, 他真的可以幫助我們很簡易的配置好我們的主機,甚至是一些很進階的配置都可以幫我們搞定。

但是鳥哥在前面的章節裏面也已經提到過相當多次了, X Window 與 web 接口的工具,他的接口雖然親善,功能雖然強大, 但畢竟他是將所有利用到的軟件都整合在一起的一組應用程序而已, 並非是一個完整的套件,所以某些時候當你升級或者是使用其他套件管理模塊 (例如 tarball 而非 rpm 文件等等) 時,就會造成配置的困擾了。甚至不同的 distribution 所設計的 X window 接口也都不相同,這樣也造成學習方面的困擾。

文字接口的 shell 就不同了!幾乎各家 distributions 使用的 bash 都是一樣的!如此一來, 你就能夠輕輕鬆鬆的轉換不同的 distributions ,就像武俠小說裏面提到的『一法通、萬法通!』

1.2.2 遠程管理:文字接口就是比較快!

此外,Linux 的管理常常需要透過遠程聯機,而聯機時文字接口的傳輸速度一定比較快, 而且,較不容易出現斷線或者是信息外流的問題,因此,shell 真的是得學習的一項工具。而且,他可以讓您更深入 Linux ,更瞭解他,而不是隻會按一按鼠標而已!所謂『天助自助者!』多摸一點文本模式的東西,會讓你與 Linux 更親近呢!

1.2.3 Linux 的任督二脈: shell 是也!

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

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

由於學習 shell 的好處真的是多多啦!所以,如果你是個系統管理員,或者有心想要管理系統的話,那麼 shell 與 shell scripts 這個東西真的有必要看一看!因爲他就像『打通任督二脈,任何武功都能隨你應用』的說!

1.3 系統的合法 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 的簡單歷史吧(注2):第一個流行的 shell 是由 Steven Bourne 發展出來的,爲了紀念他所以就稱爲 Bourne shell ,或直接簡稱爲 sh !而後來另一個廣爲流傳的 shell 是由柏克萊大學的 Bill Joy 設計依附於 BSD 版的 Unix 系統中的 shell ,這個 shell 的語法有點類似 C 語言,所以才得名爲 C shell ,簡稱爲 csh !由於在學術界 Sun 主機勢力相當的龐大,而 Sun 主要是 BSD 的分支之一,所以 C shell 也是另一個很重要而且流傳很廣的 shell 之一 。

Tips:
由於 Linux 爲 C 程序語言撰寫的,很多程序設計師使用 C 來開發軟件,因此 C shell 相對的就很熱門了。 另外,還記得我們在第一章、Linux 是什麼提到的吧? Sun 公司的創始人就是 Bill Joy,而 BSD 最早就是 Bill Joy 發展出來的啊。

那麼目前我們的 Linux (以 CentOS 5.x 爲例) 有多少我們可以使用的 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 的功能都差不多,但是在某些語法的下達方面則有所不同,因此建議你還是得要選擇某一種 shell 來熟悉一下較佳。 Linux 默認就是使用 bash ,所以最初你只要學會 bash 就非常了不起了! _! 另外,咦!爲什麼我們系統上合法的 shell 要寫入 /etc/shells 這個文件啊? 這是因爲系統某些服務在運行過程中,會去檢查使用者能夠使用的 shells ,而這些 shell 的查詢就是藉由 /etc/shells 這個文件囉!

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

那麼,再想一想,我這個使用者什麼時候可以取得 shell 來工作呢?還有, 我這個使用者默認會取得哪一個 shell 啊?還記得我們在第五章的在終端界面登陸linux小節當中提到的登陸動作吧? 當我登陸的時候,系統就會給我一個 shell 讓我來工作了。 而這個登陸取得的 shell 就記錄在 /etc/passwd 這個文件內!這個文件的內容是啥?

[root@www ~]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
.....(底下省略).....

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

1.4 Bash shell 的功能

既然 /bin/bash 是 Linux 默認的 shell ,那麼總是得了解一下這個玩意兒吧!bash 是 GNU 計劃中重要的工具軟件之一,目前也是 Linux distributions 的標準 shell 。 bash 主要兼容於 sh ,並且依據一些使用者需求,而加強的 shell 版本。不論你使用的是那個 distribution ,你都難逃需要學習 bash 的宿命啦!那麼這個 shell 有什麼好處,幹嘛 Linux 要使用他作爲默認的 shell 呢? bash 主要的優點有底下幾個:

1.4.1 命令編修能力 (history):

bash 的功能裏頭,鳥哥個人認爲相當棒的一個就是『他能記憶使用過的命令!』 這功能真的相當的棒!因爲我只要在命令列按『上下鍵』就可以找到前/後一個輸入的命令!而在很多 distribution 裏頭,默認的命令記憶功能可以到達 1000 個!也就是說, 你曾經下達過的命令幾乎都被記錄下來了。

這麼多的命令記錄在哪裏呢?在你的家目錄內的 .bash_history 啦! 不過,需要留意的是,~/.bash_history 記錄的是前一次登陸以前所運行過的命令, 而至於這一次登陸所運行的命令都被緩存在內存中,當你成功的註銷系統後,該命令記憶纔會記錄到 .bash_history 當中!

這有什麼功能呢?最大的好處就是可以『查詢曾經做過的舉動!』 如此可以知道你的運行步驟,那麼就可以追蹤你曾下達過的命令,以作爲除錯的工具! 但如此一來也有個煩惱,就是如果被黑客入侵了,那麼他只要翻你曾經運行過的命令, 剛好你的命令又跟系統有關 (例如直接輸入 MySQL 的密碼在命令列上面),那你的主機可就傷腦筋了! 到底記錄命令的數目越多還是越少越好?這部份是見仁見智啦,沒有一定的答案的。

1.4.2 命令與文件補全功能: ([tab] 按鍵的好處)

還記得我們在第五章內的重要的幾個熱鍵小節當中提到的 [tab] 這個按鍵嗎?這個按鍵的功能就是在 bash 裏頭纔有的啦!常常在 bash 環境中使用 [tab] 是個很棒的習慣喔!因爲至少可以讓你 1)少打很多字; 2)確定輸入的數據是正確的! 使用 [tab] 按鍵的時機依據 [tab] 接在命令後或參數後而有所不同。我們再複習一次:

  • [Tab] 接在一串命令的第一個字的後面,則爲命令補全;
  • [Tab] 接在一串命令的第二個字以後時,則爲『文件補齊』!

所以說,如果我想要知道我的環境中,所有可以運行的命令有幾個? 就直接在 bash 的提示字符後面連續按兩次 [tab] 按鍵就能夠顯示所有的可運行命令了。 那如果想要知道系統當中所有以 c 爲開頭的命令呢?就按下『 c[tab][tab] 』就好啦! _

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

1.4.3 命令別名配置功能: (alias)

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

alias lm=‘ls -al’

工作控制、前景背景控制: (job control, foreground, background)
這部分我們在第十七章 Linux 過程控制中再提及! 使用前、背景的控制可以讓工作進行的更爲順利!至於工作控制(jobs)的用途則更廣, 可以讓我們隨時將工作丟到背景中運行!而不怕不小心使用了 [Ctrl] + c 來停掉該程序!真是好樣的!此外,也可以在單一登錄的環境中,達到多任務的目的呢!

1.4.4 程序化腳本: (shell scripts)

在 DOS 年代還記得將一堆命令寫在一起的所謂的『批處理文件』吧?在 Linux 底下的 shell scripts 則發揮更爲強大的功能,可以將你平時管理系統常需要下達的連續命令寫成一個文件, 該文件並且可以透過對談交互式的方式來進行主機的偵測工作!也可以藉由 shell 提供的環境變量及相關命令來進行設計,哇!整個設計下來幾乎就是一個小型的程序語言了!該 scripts 的功能真的是超乎我的想象之外!以前在 DOS 底下需要程序語言才能寫的東西,在 Linux 底下使用簡單的 shell scripts 就可以幫你達成了!真的厲害!這部分我們在第十三章再來談!

1.4.5 通配符: (Wildcard)

除了完整的字符串之外, bash 還支持許多的通配符來幫助用戶查詢與命令下達。 舉例來說,想要知道 /usr/bin 底下有多少以 X 爲開頭的文件嗎?使用:『 ls -l /usr/bin/X* 』就能夠知道囉~此外,還有其他可供利用的通配符, 這些都能夠加快使用者的操作呢!

1.5 Bash shell 的內建命令: type

我們在第五章提到關於 Linux 的聯機幫助文件部分,也就是 man page 的內容,那麼 bash 有沒有什麼說明文件啊?開玩笑~ 這麼棒的東西怎麼可能沒有說明文件!請你在 shell 的環境下,直接輸入 man bash 瞧一瞧, 嘿嘿!不是蓋的吧!讓你看個幾天幾夜也無法看完的 bash 說明文件,可是很詳盡的數據啊! _

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

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

[root@www ~]# type [-tpa] name
選項與參數:
    :不加任何選項與參數時,type 會顯示出 name 是外部命令還是 bash 內建命令
-t  :當加入 -t 參數時,type 會將 name 以底下這些字眼顯示出他的意義:
      file    :表示爲外部命令;
      alias   :表示該命令爲命令別名所配置的名稱;
      builtin :表示該命令爲 bash 內建的命令功能;
-p  :如果後面接的 name 爲外部命令時,纔會顯示完整文件名;
-a  :會由 PATH 變量定義的路徑中,將所有含 name 的命令都列出來,包含 alias

範例一:查詢一下 ls 這個命令是否爲 bash 內建?
[root@www ~]# type ls
ls is aliased to `ls --color=tty' <==未加任何參數,列出 ls 的最主要使用情況
[root@www ~]# type -t ls
alias                             <==僅列出 ls 運行時的依據
[root@www ~]# type -a ls
ls is aliased to `ls --color=tty' <==最先使用 aliase
ls is /bin/ls                     <==還有找到外部命令在 /bin/ls

範例二:那麼 cd 呢?
[root@www ~]# type cd
cd is a shell builtin             <==看到了嗎? cd 是 shell 內建命令

透過 type 這個命令我們可以知道每個命令是否爲 bash 的內建命令。 此外,由於利用 type 搜尋後面的名稱時,如果後面接的名稱並不能以運行檔的狀態被找到, 那麼該名稱是不會被顯示出來的。也就是說, type 主要在找出『運行檔』而不是一般文件檔名喔! 呵呵!所以,這個 type 也可以用來作爲類似 which 命令的用途啦!找命令用的!

1.6 命令的下達

我們在第五章的開始下達命令小節已經提到過在 shell 環境下的命令下達方法,如果你忘記了請回到第五章再去回憶一下!這裏不重複說明了。 鳥哥這裏僅就反斜槓 () 來說明一下命令下達的方式囉!

範例:如果命令串太長的話,如何使用兩行來輸出?
[vbird@www ~]# cp /var/spool/mail/root /etc/crontab \
> /etc/fstab /root

上面這個命令用途是將三個文件複製到 /root 這個目錄下而已。不過,因爲命令太長, 於是鳥哥就利用『 [Enter] 』來將 [Enter] 這個按鍵『跳脫!』開來,讓 [Enter] 按鍵不再具有『開始運行』的功能!好讓命令可以繼續在下一行輸入。 需要特別留意, [Enter] 按鍵是緊接着反斜槓 () 的,兩者中間沒有其他字符。 因爲 \ 僅跳脫『緊接着的下一個字符』而已!所以,萬一我寫成: 『 \ [Enter] 』,亦即 [Enter] 與反斜槓中間有一個空格時,則 \ 跳脫的是『空格鍵』而不是 [Enter] 按鍵!這個地方請再仔細的看一遍!很重要!

如果順利跳脫 [Enter] 後,下一行最前面就會主動出現 > 的符號, 你可以繼續輸入命令囉!也就是說,那個 > 是系統自動出現的,你不需要輸入。

總之,當我們順利的在終端機 (tty) 上面登陸後, Linux 就會依據 /etc/passwd 文件的配置給我們一個 shell (默認是 bash),然後我們就可以依據上面的命令下達方式來操作 shell, 之後,我們就可以透過 man 這個在線查詢來查詢命令的使用方式與參數說明, 很不錯吧!那麼我們就趕緊更進一步來操作 bash 這個好玩的東西囉!

2. Shell 的變量功能

變量是 bash 環境中非常重要的一個玩意兒,我們知道 Linux 是多人多任務的環境,每個人登陸系統都能取得一個 bash , 每個人都能夠使用 bash 下達 mail 這個命令來收受『自己』的郵件,問題是, bash 是如何得知你的郵件信箱是哪個文件? 這就需要『變量』的幫助啦!所以,你說變量重不重要呢?底下我們將介紹重要的環境變量、變量的取用與配置等數據, 呼呼!動動腦時間又來到囉!_

2.1 什麼是變量?

那麼,什麼是『變量』呢?簡單的說,就是讓某一個特定字符串代表不固定的內容就是了。舉個大家在國中都會學到的數學例子, 那就是:『 y = ax + b 』這東西,在等號左邊的(y)就是變量,在等號右邊的(ax+b)就是變量內容。 要注意的是,左邊是未知數,右邊是已知數喔! 講的更簡單一點,我們可以『用一個簡單的 “字眼” 來取代另一個比較複雜或者是容易變動的數據』。這有什麼好處啊?最大的好處就是『方便!』。

2.1.1 變量的可變性與方便性

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

在這裏插入圖片描述
如上圖所示,由於系統已經幫我們規劃好 MAIL 這個變量,所以用戶只要知道 mail 這個命令如何使用即可, mail 會主動的取用 MAIL 這個變量,就能夠如上圖所示的取得自己的郵件信箱了!(注意大小寫,小寫的 mail 是命令, 大寫的 MAIL 則是變量名稱喔!)

那麼使用變量真的比較好嗎?這是當然的!想象一個例子,如果 mail 這個命令將 root 收信的郵件信箱 (mailbox) 檔名爲 /var/spool/mail/root 直接寫入程序代碼中。那麼當 dmtsai 要使用 mail 時,將會取得 /var/spool/mail/root 這個文件的內容! 不合理吧!所以你就需要幫 dmtsai 也設計一個 mail 的程序,將 /var/spool/mail/dmtsai 寫死到 mail 的程序代碼當中! 天吶!那系統要有多少個 mail 命令啊?反過來說,使用變量就變的很簡單了!因爲你不需要更動到程序代碼啊! 只要將 MAIL 這個變量帶入不同的內容即可讓所有使用者透過 mail 取得自己的信件!當然簡單多了!

2.1.2 影響 bash 環境操作的變量

某些特定變量會影響到 bash 的環境喔!舉例來說,我們前面已經提到過很多次的那個 PATH 變量! 你能不能在任何目錄下運行某個命令,與 PATH 這個變量有很大的關係。例如你下達 ls 這個命令時,系統就是透過 PATH 這個變量裏面的內容所記錄的路徑順序來搜尋命令的呢!如果在搜尋完 PATH 變量內的路徑還找不到 ls 這個命令時, 就會在屏幕上顯示『 command not found 』的錯誤信息了。

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

2…1.3 腳本程序設計 (shell script) 的好幫手

這些還都只是系統默認的變量的目的,如果是個人的配置方面的應用呢:例如你要寫一個大型的 script 時,有些數據因爲可能由於用戶習慣的不同而有差異,比如說路徑好了,由於該路徑在 script 被使用在相當多的地方,如果下次換了一部主機,都要修改 script 裏面的所有路徑,那麼我一定會瘋掉! 這個時候如果使用變量,而將該變量的定義寫在最前面,後面相關的路徑名稱都以變量來取代, 嘿嘿!那麼你只要修改一行就等於修改整篇 script 了!方便的很!所以,良好的程序設計師都會善用變量的定義!
在這裏插入圖片描述
最後我們就簡單的對『什麼是變量』作個簡單定義好了: 『變量就是以一組文字或符號等,來取代一些配置或者是一串保留的數據!』, 例如:我配置了『myname』就是『VBird』,所以當你讀取 myname 這個變量的時候,系統自然就會知道!哈!那就是 VBird 啦! 那麼如何『顯示變量』呢?這就需要使用到 echo 這個命令啦!

2.2 變量的取用與配置:echo, 變量配置守則, unset

說的口沫橫飛的,也不知道『變量』與『變量代表的內容』有啥關係? 那我們就將『變量』的『內容』拿出來給您瞧瞧好了。你可以利用 echo 這個命令來取用變量, 但是,變量在被取用時,前面必須要加上錢字號『 $ 』纔行,舉例來說,要知道 PATH 的內容,該如何是好?

2.2.1 變量的取用: echo

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

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

例題:
請在屏幕上面顯示出您的環境變量 HOME 與 MAIL:
答:
echo $HOME 或者是 echo ${HOME}
echo $MAIL 或者是 echo ${MAIL}

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

[root@www ~]# echo $myname
       <==這裏並沒有任何數據~因爲這個變量尚未被配置!是空的!
[root@www ~]# myname=VBird
[root@www ~]# echo $myname
VBird  <==出現了!因爲這個變量已經被配置了!

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

2.2.2 變量的配置守則

  1. 變量與變量內容以一個等號『=』來連結,如下所示:
    『myname=VBird』

  2. 等號兩邊不能直接接空格符,如下所示爲錯誤:
    『myname = VBird』或『myname=VBird Tsai』

  3. 變量名稱只能是英文字母與數字,但是開頭字符不能是數字,如下爲錯誤:
    『2myname=VBird』

  4. 變量內容若有空格符可使用雙引號『"』或單引號『’』將變量內容結合起來,但
    雙引號內的特殊字符如 $ 等,可以保有原本的特性,如下所示:
    『var=“lang is $LANG”』則『echo $var』可得『lang is en_US』
    單引號內的特殊字符則僅爲一般字符 (純文本),如下所示:
    『var=‘lang is $LANG’』則『echo $var』可得『lang is $LANG』

  5. 可用跳脫字符『 \ 』將特殊符號(如 [Enter], $, , 空格符, '等)變成一般字符;

  6. 在一串命令中,還需要藉由其他的命令提供的信息,可以使用反單引號『命令』或 『()1version=(命令)』。特別注意,那個 ` 是鍵盤上方的數字鍵 1 左邊那個按鍵,而不是單引號! 例如想要取得核心版本的配置: 『version=(uname -r)』再『echo $version』可得『2.6.18-128.el5』

  7. 若該變量爲擴增變量內容時,則可用 “$變量名稱” 或 PATH="{變量} 累加內容,如下所示: 『PATH="PATH":/home/bin』

  8. 若該變量需要在其他子程序運行,則需要以 export 來使變量變成環境變量:
    『export PATH』

  9. 通常大寫字符爲系統默認變量,自行配置變量可以使用小寫字符,方便判斷 (純粹依照使用者興趣與嗜好) ;

  10. 取消變量的方法爲使用 unset :『unset 變量名稱』例如取消 myname 的配置:
    『unset myname』

底下讓鳥哥舉幾個例子來讓你試看看,就知道怎麼配置好你的變量囉!

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

範例二:承上題,若變量內容爲 VBird's name 呢,就是變量內容含有特殊符號時:
[root@www ~]# name=VBird's name  
# 單引號與雙引號必須要成對,在上面的配置中僅有一個單引號,因此當你按下 enter 後,
# 你還可以繼續輸入變量內容。這與我們所需要的功能不同,失敗啦!
# 記得,失敗後要復原請按下 [ctrl]-c 結束!
[root@www ~]# name="VBird's name"    <==OK 的啦!
# 命令是由左邊向右找→,先遇到的引號先有用,因此如上所示,單引號會失效!
[root@www ~]# name='VBird's name'    <==失敗的啦!
# 因爲前兩個單引號已成對,後面就多了一個不成對的單引號了!因此也就失敗了!
[root@www ~]# name=VBird\'s\ name     <==OK 的啦!
# 利用反斜槓 (\) 跳脫特殊字符,例如單引號與空格鍵,這也是 OK 的啦!

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

範例四:承範例三,我要將 name 的內容多出 "yes" 呢?
[root@www ~]# name=$nameyes  
# 知道了吧?如果沒有雙引號,那麼變量成了啥?name 的內容是 $nameyes 這個變量!
# 呵呵!我們可沒有配置過 nameyes 這個變量吶!所以,應該是底下這樣纔對!
[root@www ~]# name="$name"yes
[root@www ~]# name=${name}yes  <==以此例較佳!

範例五:如何讓我剛剛配置的 name=VBird 可以用在下個 shell 的程序?
[root@www ~]# name=VBird
[root@www ~]# bash        <==進入到所謂的子程序
[root@www ~]# echo $name  <==子程序:再次的 echo 一下;
       <==嘿嘿!並沒有剛剛配置的內容喔!
[root@www ~]# exit        <==子程序:離開這個子程序
[root@www ~]# export name
[root@www ~]# bash        <==進入到所謂的子程序
[root@www ~]# echo $name  <==子程序:在此運行!
VBird  <==看吧!出現配置值了!
[root@www ~]# exit        <==子程序:離開這個子程序

什麼是『子程序』呢?就是說,在我目前這個 shell 的情況下,去激活另一個新的 shell ,新的那個 shell 就是子程序啦!在一般的狀態下,父程序的自定義變量是無法在子程序內使用的。但是透過 export 將變量變成環境變量後,就能夠在子程序底下應用了!很不賴吧!至於程序的相關概念, 我們會在第十七章程序管理當中提到的喔!

範例六:如何進入到您目前核心的模塊目錄?
[root@www ~]# cd /lib/modules/`uname -r`/kernel
[root@www ~]# cd /lib/modules/$(uname -r)/kernel

每個 Linux 都能夠擁有多個核心版本,且幾乎 distribution 的核心版本都不相同。以 CentOS 5.3 (未升級前) 爲例,他的默認核心版本是 2.6.18-128.el5 ,所以核心模塊目錄在 /lib/modules/2.6.18-128.el5/kernel/ 內。 也由於每個 distributions 的這個值都不相同,但是我們卻可以利用 uname -r 這個命令先取得版本信息。所以囉,就可以透過上面命令當中的內含命令 uname -r 先取得版本輸出到 cd … 那個命令當中,就能夠順利的進入目前核心的驅動程序所放置的目錄囉!很方便吧!

其實上面的命令可以說是作了兩次動作,亦即是:

  1. 先進行反單引號內的動作『uname -r』並得到核心版本爲 2.6.18-128.el5
  2. 將上述的結果帶入原命令,故得命令爲:『cd /lib/modules/2.6.18-128.el5/kernel/』
範例七:取消剛剛配置的 name 這個變量內容
[root@www ~]# unset name

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

例題:
在變量的配置當中,單引號與雙引號的用途有何不同?
答:
單引號與雙引號的最大不同在於雙引號仍然可以保有變量的內容,但單引號內僅能是一般字符 ,而不會有特殊符號。我們以底下的例子做說明:假設您定義了一個變量, name=VBird ,現在想以 name 這個變量的內容定義出 myname 顯示 VBird its me 這個內容,要如何訂定呢?
[root@www ~]# name=VBird
[root@www ~]# echo KaTeX parse error: Expected 'EOF', got '#' at position 24: …rd [root@www ~]#̲ myname="name its me"
[root@www ~]# echo KaTeX parse error: Expected 'EOF', got '#' at position 33: …me [root@www ~]#̲ myname='name its me’
[root@www ~]# echo $myname
$name its me
發現了嗎?沒錯!使用了單引號的時候,那麼 $name 將失去原有的變量內容,僅爲一般字符的顯示型態而已!這裏必需要特別小心在意!

例題:
在命令下達的過程中,反單引號( ` )這個符號代表的意義爲何?
答:
在一串命令中,在 ` 之內的命令將會被先運行,而其運行出來的結果將做爲外部的輸入信息!例如 uname -r 會顯示出目前的核心版本,而我們的核心版本在 /lib/modules 裏面,因此,你可以先運行 uname -r 找出核心版本,然後再以『 cd 目錄』到該目錄下,當然也可以運行如同上面範例六的運行內容囉。
另外再舉個例子,我們也知道, locate 命令可以列出所有的相關文件檔名,但是,如果我想要知道各個文件的權限呢?舉例來說,我想要知道每個 crontab 相關檔名的權限:
[root@www ~]# ls -l locate crontab
如此一來,先以 locate 將文件名數據都列出來,再以 ls 命令來處理的意思啦!瞭了嗎? _

例題:
若你有一個常去的工作目錄名稱爲:『/cluster/server/work/taiwan_2005/003/』,如何進行該目錄的簡化?
答:
在一般的情況下,如果你想要進入上述的目錄得要『cd /cluster/server/work/taiwan_2005/003/』, 以鳥哥自己的案例來說,鳥哥跑數值模式常常會配置很長的目錄名稱(避免忘記),但如此一來變換目錄就很麻煩。 此時,鳥哥習慣利用底下的方式來降低命令下達錯誤的問題:
[root@www ~]# work="/cluster/server/work/taiwan_2005/003/"
[root@www ~]# cd $work
未來我想要使用其他目錄作爲我的模式工作目錄時,只要變更 work 這個變量即可!而這個變量又可以在 bash 的配置文件中直接指定,那我每次登陸只要運行『 cd $work 』就能夠去到數值模式仿真的工作目錄了!是否很方便呢? _

Tips:
老實說,使用『 version=$(uname -r) 』來取代『 version=uname -r 』比較好,因爲反單引號大家老是容易打錯或看錯! 所以現在鳥哥都習慣使用 $( 命令 ) 來介紹這個功能!

2.3 環境變量的功能: env 與常見環境變量說明, set, export

環境變量可以幫我們達到很多功能~包括家目錄的變換啊、提示字符的顯示啊、運行文件搜尋的路徑啊等等的, 還有很多很多啦!那麼,既然環境變量有那麼多的功能,問一下,目前我的 shell 環境中, 有多少默認的環境變量啊?我們可以利用兩個命令來查閱,分別是 env 與 export 呢!

2.3.1 用 env 觀察環境變量與常見環境變量說明

範例一:列出目前的 shell 環境下的所有環境變量與其內容。
[root@www ~]# env
HOSTNAME=www.vbird.tsai    <== 這部主機的主機名
TERM=xterm                 <== 這個終端機使用的環境是什麼類型
SHELL=/bin/bash            <== 目前這個環境下,使用的 Shell 是哪一個程序?
HISTSIZE=1000              <== 『記錄命令的筆數』在 CentOS 默認可記錄 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: <== 一些顏色顯示
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                 <== 這個與語系有關,底下會再介紹!
HOME=/root                 <== 這個用戶的家目錄啊!
_=/bin/env                 <== 上一次使用的命令的最後一個參數(或命令本身)

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

  • HOME
    代表用戶的家目錄。還記得我們可以使用 cd ~ 去到自己的家目錄嗎?或者利用 cd 就可以直接回到用戶家目錄了。那就是取用這個變量啦~ 有很多程序都可能會取用到這個變量的值!

  • SHELL
    告知我們,目前這個環境使用的 SHELL 是哪支程序? Linux 默認使用 /bin/bash 的啦!

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

  • 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@www ~]# declare -i number=$RANDOM*10/32768 ; echo $number
8   <== 此時會隨機取出 0~9 之間的數值喔!

大致上是有這些環境變量啦~裏面有些比較重要的參數,在底下我們都會另外進行一些說明的~

2.3.2 用 set 觀察所有變量 (含環境變量與自定義變量)

bash 可不只有環境變量喔,還有一些與 bash 操作接口有關的變量,以及用戶自己定義的變量存在的。 那麼這些變量如何觀察呢?這個時候就得要使用 set 這個命令了。 set 除了環境變量之外, 還會將其他在 bash 內的變量通通顯示出來哩!信息很多,底下鳥哥僅列出幾個重要的內容:

[root@www ~]# set
BASH=/bin/bash           <== bash 的主程序放置路徑
BASH_VERSINFO=([0]="3" [1]="2" [2]="25" [3]="1" [4]="release" 
[5]="i686-redhat-linux-gnu")      <== bash 的版本啊!
BASH_VERSION='3.2.25(1)-release'  <== 也是 bash 的版本啊!
COLORS=/etc/DIR_COLORS.xterm      <== 使用的顏色紀錄文件
COLUMNS=115              <== 在目前的終端機環境下,使用的字段有幾個字符長度
HISTFILE=/root/.bash_history      <== 歷史命令記錄的放置文件,隱藏檔
HISTFILESIZE=1000        <== 存起來(與上個變量有關)的文件之命令的最大紀錄筆數。
HISTSIZE=1000            <== 目前環境下,可記錄的歷史命令最大筆數。
HOSTTYPE=i686            <== 主機安裝的軟件主要類型。我們用的是 i686 兼容機器軟件
IFS=$' \t\n'             <== 默認的分隔符
LINES=35                 <== 目前的終端機下的最大行數
MACHTYPE=i686-redhat-linux-gnu    <== 安裝的機器類型
MAILCHECK=60             <== 與郵件有關。每 60 秒去掃瞄一次信箱有無新信!
OLDPWD=/home             <== 上個工作目錄。我們可以用 cd - 來取用這個變量。
OSTYPE=linux-gnu         <== 操作系統的類型!
PPID=20025               <== 父程序的 PID (會在後續章節才介紹)
PS1='[\u@\h \W]\$ '      <== PS1 就厲害了。這個是命令提示字符,也就是我們常見的
                             [root@www ~]# 或 [dmtsai ~]$ 的配置值啦!可以更動的!
PS2='> '                 <== 如果你使用跳脫符號 (\) 第二行以後的提示字符也
name=VBird               <== 剛剛配置的自定義變量也可以被列出來喔!
$                        <== 目前這個 shell 所使用的 PID
?                        <== 剛剛運行完命令的回傳值。

一般來說,不論是否爲環境變量,只要跟我們目前這個 shell 的操作接口有關的變量, 通常都會被配置爲大寫字符,也就是說,『基本上,在 Linux 默認的情況中,使用{大寫的字母}來配置的變量一般爲系統內定需要的變量』。 OK!OK!那麼上頭那些變量當中,有哪些是比較重要的?大概有這幾個吧!

  • PS1:(提示字符的配置)
    這是 PS1 (數字的 1 不是英文字母),這個東西就是我們的『命令提示字符』喔! 當我們每次按下 [Enter] 按鍵去運行某個命令後,最後要再次出現提示字符時, 就會主動去讀取這個變量值了。上頭 PS1 內顯示的是一些特殊符號,這些特殊符號可以顯示不同的信息, 每個 distributions 的 bash 默認的 PS1 變量內容可能有些許的差異,不要緊,『習慣你自己的習慣』就好了。 你可以用 man bash (注3)去查詢一下 PS1 的相關說明,以理解底下的一些符號意義。

    • \d :可顯示出『星期 月 日』的日期格式,如:“Mon Feb 2”
    • \H :完整的主機名。舉例來說,鳥哥的練習機爲『www.vbird.tsai』
    • \h :僅取主機名在第一個小數點之前的名字,如鳥哥主機則爲『www』後面省略
    • \t :顯示時間,爲 24 小時格式的『HH:MM:SS』
    • \T :顯示時間,爲 12 小時格式的『HH:MM:SS』
    • \A :顯示時間,爲 24 小時格式的『HH:MM』
    • @ :顯示時間,爲 12 小時格式的『am/pm』樣式
    • \u :目前使用者的賬號名稱,如『root』;
    • \v :BASH 的版本信息,如鳥哥的測試主板本爲 3.2.25(1),僅取『3.2』顯示
    • \w :完整的工作目錄名稱,由根目錄寫起的目錄名稱。但家目錄會以 ~ 取代;
    • \W :利用 basename 函數取得工作目錄名稱,所以僅會列出最後一個目錄名。
    • # :下達的第幾個命令。
    • $ :提示字符,如果是 root 時,提示字符爲 # ,否則就是 $ 囉~

好了,讓我們來看看 CentOS 默認的 PS1 內容吧:『[\u@\h \W]$ 』,現在你知道那些反斜槓後的數據意義了吧? 要注意喔!那個反斜槓後的數據爲 PS1 的特殊功能,與 bash 的變量配置沒關係啦!不要搞混了喔! 那你現在知道爲何你的命令提示字符是:『 [root@www ~]# 』了吧? 好了,那麼假設我想要有類似底下的提示字符:
[root@www /home/dmtsai 16:50 #12]#
那個 # 代表第 12 次下達的命令。那麼應該如何配置 PS1 呢?可以這樣啊:

[root@www ~ ]# cd /home
[root@www home]# PS1='[\u@\h \w \A #\#]\$ '
[root@www /home 17:02 #85]# 
# 看到了嗎?提示字符變了!變的很有趣吧!其中,那個 #85 比較有趣,
# 如果您再隨便輸入幾次 ls 後,該數字就會添加喔!爲啥?上面有說明滴!
  • $:(關於本 shell 的 PID)
    錢字號本身也是個變量喔!這個咚咚代表的是『目前這個 Shell 的線程代號』,亦即是所謂的 PID (Process ID)。 更多的程序觀念,我們會在第四篇的時候提及。想要知道我們的 shell 的 PID ,就可以用:『 echo $$ 』即可!出現的數字就是你的 PID 號碼。

  • ?:(關於上個運行命令的回傳值)
    什麼?問號也是一個特殊的變量?沒錯!在 bash 裏面這個變量可重要的很! 這個變量是:『上一個運行的命令所回傳的值』, 上面這句話的重點是『上一個命令』與『回傳值』兩個地方。當我們運行某些命令時, 這些命令都會回傳一個運行後的代碼。一般來說,如果成功的運行該命令, 則會回傳一個 0 值,如果運行過程發生錯誤,就會回傳『錯誤代碼』纔對!一般就是以非爲 0 的數值來取代。 我們以底下的例子來看看:

[root@www ~]# echo $SHELL
/bin/bash                                  <==可順利顯示!沒有錯誤!
[root@www ~]# echo $?
0                                          <==因爲沒問題,所以回傳值爲 0
[root@www ~]# 12name=VBird
-bash: 12name=VBird: command not found     <==發生錯誤了!bash回報有問題
[root@www ~]# echo $?
127                                        <==因爲有問題,回傳錯誤代碼(非爲0)
# 錯誤代碼回傳值依據軟件而有不同,我們可以利用這個代碼來搜尋錯誤的原因喔!
[root@www ~]# echo $?
0
# 咦!怎麼又變成正確了?這是因爲 "?" 只與『上一個運行命令』有關,
# 所以,我們上一個命令是運行『 echo $? 』,當然沒有錯誤,所以是 0 沒錯!

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

我們在第零章、計算器概論內的 CPU 等級說明中談過 CPU , 目前個人計算機的 CPU 主要分爲 32/64 位,其中 32 位又可分爲 i386, i586, i686,而 64 位則稱爲 x86_64。 由於不同等級的 CPU 命令集不太相同,因此你的軟件可能會針對某些 CPU 進行優化,以求取較佳的軟件性能。 所以軟件就有 i386, i686 及 x86_64 之分。以目前 (2009) 的主流硬件來說,幾乎都是 x86_64 的天下! 但是畢竟舊機器還是非常多,以鳥哥的環境來說,我用 P-III 等級的計算機,所以上頭就發現我的等級是 i686 啦!

要留意的是,較高階的硬件通常會向下兼容舊有的軟件,但較高階的軟件可能無法在舊機器上面安裝! 我們在第三章就曾說明過, 這裏再強調一次,你可以在 x86_64 的硬件上安裝 i386 的 Linux 操作系統,但是你無法在 i686 的硬件上安裝 x86_64 的 Linux 操作系統!這點得要牢記在心!

2.3.4 export: 自定義變量轉成環境變量

談了 env 與 set 現在知道有所謂的環境變量與自定義變量,那麼這兩者之間有啥差異呢?其實這兩者的差異在於『 該變量是否會被子程序所繼續引用』啦!唔!那麼啥是父程序?子程序? 這就得要了解一下命令的下達行爲了。

當你登陸 Linux 並取得一個 bash 之後,你的 bash 就是一個獨立的程序,被稱爲 PID 的就是。 接下來你在這個 bash 底下所下達的任何命令都是由這個 bash 所衍生出來的,那些被下達的命令就被稱爲子程序了。 我們可以用底下的圖示來簡單的說明一下父程序與子程序的概念:
在這裏插入圖片描述
如上所示,我們在原本的 bash 底下運行另一個 bash ,結果操作的環境接口會跑到第二個 bash 去(就是子程序), 那原本的 bash 就會在暫停的情況 (睡着了,就是 sleep)。整個命令運行的環境是實線的部分!若要回到原本的 bash 去, 就只有將第二個 bash 結束掉 (下達 exit 或 logout) 纔行。更多的程序概念我們會在第四篇談及,這裏只要有這個概念即可。

這個程序概念與變量有啥關係啊?關係可大了!因爲子程序僅會繼承父程序的環境變量, 子程序不會繼承父程序的自定義變量啦!所以你在原本 bash 的自定義變量在進入了子程序後就會消失不見, 一直到你離開子程序並回到原本的父程序後,這個變量纔會又出現!

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

[root@www ~]# export 變量名稱

這東西用在『分享自己的變量配置給後來呼叫的文件或其他程序』啦! 像鳥哥常常在自己的主控文件後面呼叫其他附屬文件(類似函式的功能),但是主控文件與附屬文件內都有相同的變量名稱, 若一再重複配置時,要修改也很麻煩,此時只要在原本的第一個文件內配置好『 export 變量 』, 後面所呼叫的文件就能夠使用這個變量配置了!而不需要重複配置,這非常實用於 shell script 當中喔! 如果僅下達 export 而沒有接變量時,那麼此時將會把所有的『環境變量』秀出來喔!例如:

[root@www ~]# export
declare -x HISTSIZE="1000"
declare -x HOME="/root"
declare -x HOSTNAME="www.vbird.tsai"
declare -x INPUTRC="/etc/inputrc"
declare -x LANG="en_US"
declare -x LOGNAME="root"
# 後面的鳥哥就都直接省略了!不然....浪費版面~ ^_^

那如何將環境變量轉成自定義變量呢?可以使用本章後續介紹的 declare 呢!

2.4 影響顯示結果的語系變量 (locale)

還記得我們在第五章裏面提到的語系問題嗎? 就是當我們使用 man command 的方式去查詢某個數據的說明文件時,該說明檔的內容可能會因爲我們使用的語系不同而產生亂碼。 另外,利用 ls 查詢文件的時間時,也可能會有亂碼出現在時間的部分。那個問題其實就是語系的問題啦。

目前大多數的 Linux distributions 已經都是支持日漸流行的萬國碼了,也都支持大部分的國家語系。 這有賴於 i18n (注4) 支持的幫助呢! 那麼我們的 Linux 到底支持了多少的語系呢?這可以由 locale 這個命令來查詢到喔!

[root@www ~]# locale -a
....(前面省略)....
zh_TW
zh_TW.big5     <==大五碼的中文編碼
zh_TW.euctw
zh_TW.utf8     <==萬國碼的中文編碼
zu_ZA
zu_ZA.iso88591
zu_ZA.utf8

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

[root@www ~]# locale  <==後面不加任何選項與參數即可!
LANG=en_US                   <==主語言的環境
LC_CTYPE="en_US"             <==字符(文字)辨識的編碼
LC_NUMERIC="en_US"           <==數字系統的顯示信息
LC_TIME="en_US"              <==時間系統的顯示數據
LC_COLLATE="en_US"           <==字符串的比較與排序等
LC_MONETARY="en_US"          <==幣值格式的顯示等
LC_MESSAGES="en_US"          <==信息顯示的內容,如菜單、錯誤信息等
LC_ALL=                      <==整體語系的環境
....(後面省略)....

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

因爲在 Linux 主機的終端機接口環境下是無法顯示像中文這麼複雜的編碼文字, 所以就會產生亂碼了。也就是如此,我們纔會必須要在 tty1 ~ tty6 的環境下, 加裝一些中文化接口的軟件,才能夠看到中文啊!不過,如果你是在 MS Windows 主機以遠程聯機服務器的軟件聯機到主機的話,那麼,嘿嘿!其實文字接口確實是可以看到中文的。 此時反而你得要在 LANG 配置中文編碼纔好呢!

Tips:
無論如何,如果發生一些亂碼的問題,那麼配置系統裏面保有的語系編碼, 例如: en_US 或 en_US.utf8 等等的配置,應該就 OK 的啦!好了,那麼系統默認支持多少種語系呢? 當我們使用 locale 時,系統是列出目前 Linux 主機內保有的語系文件, 這些語系文件都放置在: /usr/lib/locale/ 這個目錄中。

你當然可以讓每個使用者自己去調整自己喜好的語系,但是整體系統默認的語系定義在哪裏呢? 其實就是在 /etc/sysconfig/i18n 囉!這個文件在 CentOS 5.x 的內容有點像這樣:

[root@www ~]# cat /etc/sysconfig/i18n
LANG="zh_TW.UTF-8"

因爲鳥哥在第四章的安裝時選擇的是中文語系安裝畫面, 所以這個文件默認就會使用中文編碼啦!你也可以自行將他改成你想要的語系編碼即可。

Tips:
假設你有一個純文本文件原本是在 Windows 底下創建的,那麼這個文件默認應該是 big5 的編碼格式。 在你將這個文件上傳到 Linux 主機後,在 X window 底下打開時,咦!怎麼中文字通通變成亂碼了? 別擔心!因爲如上所示, i18n 默認是萬國碼系統嘛!你只要將開啓該文件的軟件編碼由 utf8 改成 big5 就能夠看到正確的中文了!

2.5 變量的有效範圍

什麼?變量也有使用的『範圍』?沒錯啊~我們在上頭的 export 命令說明中,就提到了這個概念了。如果在跑程序的時候,有父程序與子程序的不同程序關係時, 則『變量』可否被引用與 export 有關。被 export 後的變量,我們可以稱他爲『環境變量』! 環境變量可以被子程序所引用,但是其他的自定義變量內容就不會存在於子程序中。

Tips:
在某些不同的書籍會談到『全局變量, global variable』與『局部變量, local variable』。 基本上你可以這樣看待:
環境變量=全局變量
自定義變量=局部變量

在學理方面,爲什麼環境變量的數據可以被子程序所引用呢?這是因爲內存配置的關係!理論上是這樣的:

  • 當啓動一個 shell,操作系統會分配一記憶區塊給 shell 使用,此內存內之變量可讓子程序取用
  • 若在父程序利用 export 功能,可以讓自定義變量的內容寫到上述的記憶區塊當中(環境變量);
  • 當加載另一個 shell 時 (亦即啓動子程序,而離開原本的父程序了),子 shell 可以將父 shell 的環境變量所在的記憶區塊導入自己的環境變量區塊當中。

透過這樣的關係,我們就可以讓某些變量在相關的程序之間存在,以幫助自己更方便的操作環境喔! 不過要提醒的是,這個『環境變量』與『bash 的操作環境』意思不太一樣,舉例來說, PS1 並不是環境變量, 但是這個 PS1 會影響到 bash 的接口 (提示字符嘛)!相關性要釐清喔!_

2.6 變量鍵盤讀取、數組與宣告: read, declare, array

我們上面提到的變量配置功能,都是由命令列直接配置的,那麼,可不可以讓用戶能夠經由鍵盤輸入? 什麼意思呢?是否記得某些程序運行的過程當中,會等待使用者輸入 “yes/no” 之類的信息啊? 在 bash 裏面也有相對應的功能喔!此外,我們還可以宣告這個變量的屬性, 例如:數組或者是數字等等的。底下就來看看吧!

2.6.1 read

要讀取來自鍵盤輸入的變量,就是用 read 這個命令了。這個命令最常被用在 shell script 的撰寫當中, 想要跟使用者對談?用這個命令就對了。關於 script 的寫法,我們會在第十三章介紹,底下先來瞧一瞧 read 的相關語法吧!

[root@www ~]# read [-pt] variable
選項與參數:
-p  :後面可以接提示字符!
-t  :後面可以接等待的『秒數!』這個比較有趣~不會一直等待使用者啦!

範例一:讓用戶由鍵盤輸入一內容,將該內容變成名爲 atest 的變量
[root@www ~]# read atest
This is a test        <==此時光標會等待你輸入!請輸入左側文字看看
[root@www ~]# echo $atest
This is a test          <==你剛剛輸入的數據已經變成一個變量內容!

範例二:提示使用者 30 秒內輸入自己的大名,將該輸入字符串作爲名爲 named 的變量內容
[root@www ~]# read -p "Please keyin your name: " -t 30 named
Please keyin your name: VBird Tsai   <==注意看,會有提示字符喔!
[root@www ~]# echo $named
VBird Tsai        <==輸入的數據又變成一個變量的內容了!

read 之後不加任何參數,直接加上變量名稱,那麼底下就會主動出現一個空白行等待你的輸入(如範例一)。 如果加上 -t 後面接秒數,例如上面的範例二,那麼 30 秒之內沒有任何動作時, 該命令就會自動略過了~如果是加上 -p ,嘿嘿!在輸入的光標前就會有比較多可以用的提示字符給我們參考! 在命令的下達裏面,比較美觀啦! _

2.6.2 declare / typeset

declare 或 typeset 是一樣的功能,就是在『宣告變量的類型』。如果使用 declare 後面並沒有接任何參數,那麼 bash 就會主動的將所有的變量名稱與內容通通叫出來,就好像使用 set 一樣啦! 那麼 declare 還有什麼語法呢?看看先:

[root@www ~]# declare [-aixr] variable
選項與參數:
-a  :將後面名爲 variable 的變量定義成爲數組 (array) 類型
-i  :將後面名爲 variable 的變量定義成爲整數數字 (integer) 類型
-x  :用法與 export 一樣,就是將後面的 variable 變成環境變量;
-r  :將變量配置成爲 readonly 類型,該變量不可被更改內容,也不能 unset

範例一:讓變量 sum 進行 100+300+50 的加總結果
[root@www ~]# sum=100+300+50
[root@www ~]# echo $sum
100+300+50  <==咦!怎麼沒有幫我計算加總?因爲這是文字型態的變量屬性啊!
[root@www ~]# declare -i sum=100+300+50
[root@www ~]# echo $sum
450         <==瞭乎??

由於在默認的情況底下, bash 對於變量有幾個基本的定義:

  • 變量類型默認爲『字符串』,所以若不指定變量類型,則 1+2 爲一個『字符串』而不是『計算式』。 所以上述第一個運行的結果纔會出現那個情況的;
  • bash 環境中的數值運算,默認最多僅能到達整數形態,所以 1/3 結果是 0;
    現在你曉得爲啥你需要進行變量宣告了吧?如果需要非字符串類型的變量,那就得要進行變量的宣告纔行啦! 底下繼續來玩些其他的 declare 功能。
範例二:將 sum 變成環境變量
[root@www ~]# declare -x sum
[root@www ~]# export | grep sum
declare -ix sum="450"  <==果然出現了!包括有 i 與 x 的宣告!

範例三:讓 sum 變成只讀屬性,不可更動!
[root@www ~]# declare -r sum
[root@www ~]# sum=tesgting
-bash: sum: readonly variable  <==老天爺~不能改這個變量了!

範例四:讓 sum 變成非環境變量的自定義變量吧!
[root@www ~]# declare +x sum  <== 將 - 變成 + 可以進行『取消』動作
[root@www ~]# declare -p sum  <== -p 可以單獨列出變量的類型
declare -ir sum="450" <== 看吧!只剩下 i, r 的類型,不具有 x 囉!

declare 也是個很有用的功能~尤其是當我們需要使用到底下的數組功能時, 他也可以幫我們宣告數組的屬性喔!不過,老話一句,數組也是在 shell script 比較常用的啦! 比較有趣的是,如果你不小心將變量配置爲『只讀』,通常得要註銷再登陸才能復原該變量的類型了! @_@

2.6.3 數組 (array) 變量類型

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

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

範例:配置上面提到的 var[1] ~ var[3] 的變量。
[root@www ~]# var[1]="small min"
[root@www ~]# var[2]="big min"
[root@www ~]# var[3]="nice min"
[root@www ~]# echo "${var[1]}, ${var[2]}, ${var[3]}"
small min, big min, nice min

數組的變量類型比較有趣的地方在於『讀取』,一般來說,建議直接以 ${數組} 的方式來讀取,比較正確無誤的啦!

2.7 與文件系統及程序的限制關係: ulimit

想象一個狀況:我的 Linux 主機裏面同時登陸了十個人,這十個人不知怎麼搞的, 同時開啓了 100 個文件,每個文件的大小約 10MBytes ,請問一下, 我的 Linux 主機的內存要有多大才夠? 1010010 = 10000 MBytes = 10GBytes … 老天爺,這樣,系統不掛點纔有鬼哩!爲了要預防這個情況的發生,所以我們的 bash 是可以『限制用戶的某些系統資源』的,包括可以開啓的文件數量, 可以使用的 CPU 時間,可以使用的內存總量等等。如何配置?用 ulimit 吧!

[root@www ~]# ulimit [-SHacdfltu] [配額]
選項與參數:
-H  :hard limit ,嚴格的配置,必定不能超過這個配置的數值;
-S  :soft limit ,警告的配置,可以超過這個配置值,但是若超過則有警告信息。
      在配置上,通常 soft 會比 hard 小,舉例來說,soft 可配置爲 80 而 hard 
      配置爲 100,那麼你可以使用到 90 (因爲沒有超過 100),但介於 80~100 之間時,
      系統會有警告信息通知你!
-a  :後面不接任何選項與參數,可列出所有的限制額度;
-c  :當某些程序發生錯誤時,系統可能會將該程序在內存中的信息寫成文件(除錯用),
      這種文件就被稱爲核心文件(core file)。此爲限制每個核心文件的最大容量。
-f  :此 shell 可以創建的最大文件容量(一般可能配置爲 2GB)單位爲 Kbytes
-d  :程序可使用的最大斷裂內存(segment)容量;
-l  :可用於鎖定 (lock) 的內存量
-t  :可使用的最大 CPU 時間 (單位爲秒)
-u  :單一用戶可以使用的最大程序(process)數量。

範例一:列出你目前身份(假設爲root)的所有限制數據數值
[root@www ~]# ulimit -a
core file size          (blocks, -c) 0          <==只要是 0 就代表沒限制
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited  <==可創建的單一文件的大小
pending signals                 (-i) 11774
max locked memory       (kbytes, -l) 32
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024       <==同時可開啓的文件數量
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 11774
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

範例二:限制用戶僅能創建 10MBytes 以下的容量的文件
[root@www ~]# ulimit -f 10240
[root@www ~]# ulimit -a
file size               (blocks, -f) 10240 <==最大量爲10240Kbyes,相當10Mbytes
[root@www ~]# dd if=/dev/zero of=123 bs=1M count=20
File size limit exceeded <==嘗試創建 20MB 的文件,結果失敗了!

還記得我們在第八章 Linux 磁盤文件系統裏面提到過,單一 filesystem 能夠支持的單一文件大小與 block 的大小有關。例如 block size 爲 1024 byte 時,單一文件可達 16GB 的容量。但是,我們可以用 ulimit 來限制使用者可以創建的文件大小喔! 利用 ulimit -f 就可以來配置了!例如上面的範例二,要注意單位喔!單位是 Kbytes。 若改天你一直無法創建一個大容量的文件,記得瞧一瞧 ulimit 的信息喔!

Tips:
想要復原 ulimit 的配置最簡單的方法就是註銷再登陸,否則就是得要重新以 ulimit 配置纔行! 不過,要注意的是,一般身份使用者如果以 ulimit 配置了 -f 的文件大小, 那麼他『只能繼續減小文件容量,不能添加文件容量喔!』另外,若想要管控使用者的 ulimit 限值, 可以參考第十四章的 pam 的介紹。

2.8 變量內容的刪除、取代與替換,刪除與取代, 測試與替換

變量除了可以直接配置來修改原本的內容之外,有沒有辦法透過簡單的動作來將變量的內容進行微調呢? 舉例來說,進行變量內容的刪除、取代與替換等!是可以的!我們可以透過幾個簡單的小步驟來進行變量內容的微調喔! 底下就來試試看!

2.8.1 變量內容的刪除與取代

變量的內容可以很簡單的透過幾個咚咚來進行刪除喔!我們使用 PATH 這個變量的內容來做測試好了。 請你依序進行底下的幾個例子來玩玩,比較容易感受的到鳥哥在這裏想要表達的意義:

範例一:先讓小寫的 path 自定義變量配置的與 PATH 內容相同
[root@www ~]# path=${PATH}
[root@www ~]# echo $path
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:
/usr/sbin:/usr/bin:/root/bin  <==這兩行其實是同一行啦!

範例二:假設我不喜歡 kerberos,所以要將前兩個目錄刪除掉,如何顯示?
[root@www ~]# echo ${path#/*kerberos/bin:}
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin

上面這個範例很有趣的!他的重點可以用底下這張表格來說明:

${variable#/*kerberos/bin:}
   上面的特殊字體部分是關鍵詞!用在這種刪除模式所必須存在的

${variable#/*kerberos/bin:}
   這就是原本的變量名稱,以上面範例二來說,這裏就填寫 path 這個『變量名稱』啦!

${variable#/*kerberos/bin:}
   這是重點!代表『從變量內容的最前面開始向右刪除』,且僅刪除最短的那個

${variable#/*kerberos/bin:}
   代表要被刪除的部分,由於 # 代表由前面開始刪除,所以這裏便由開始的 / 寫起。
   需要注意的是,我們還可以透過通配符 * 來取代 0 到無窮多個任意字符

   以上面範例二的結果來看, path 這個變量被刪除的內容如下所示:
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:
/usr/sbin:/usr/bin:/root/bin  <==這兩行其實是同一行啦!

很有趣吧!這樣瞭解了 # 的功能了嗎?接下來讓我們來看看底下的範例三!

範例三:我想要刪除前面所有的目錄,僅保留最後一個目錄
[root@www ~]# echo ${path#/*:}
/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:
/root/bin     <==這兩行其實是同一行啦!
# 由於一個 # 僅刪除掉最短的那個,因此他刪除的情況可以用底下的刪除線來看:
# /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:
# /usr/sbin:/usr/bin:/root/bin  <==這兩行其實是同一行啦!

[root@www ~]# echo ${path##/*:}
/root/bin
# 嘿!多加了一個 # 變成 ## 之後,他變成『刪除掉最長的那個數據』!亦即是:
# /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:
# /usr/sbin:/usr/bin:/root/bin  <==這兩行其實是同一行啦!

非常有趣!不是嗎?因爲在 PATH 這個變量的內容中,每個目錄都是以冒號『:』隔開的, 所以要從頭刪除掉目錄就是介於斜線 (/) 到冒號 (😃 之間的數據!但是 PATH 中不止一個冒號 (😃 啊! 所以 # 與 ## 就分別代表:

  • # :符合取代文字的『最短的』那一個;
  • ##:符合取代文字的『最長的』那一個

上面談到的是『從前面開始刪除變量內容』,那麼如果想要『從後面向前刪除變量內容』呢? 這個時候就得使用百分比 (%) 符號了!來看看範例四怎麼做吧!

範例四:我想要刪除最後面那個目錄,亦即從 : 到 bin 爲止的字符串
[root@www ~]# echo ${path%:*bin}
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:
/usr/sbin:/usr/bin  <==注意啊!最後面一個目錄不見去!
# 這個 % 符號代表由最後面開始向前刪除!所以上面得到的結果其實是來自如下:
# /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:
# /usr/sbin:/usr/bin:/root/bin  <==這兩行其實是同一行啦!

範例五:那如果我只想要保留第一個目錄呢?
[root@www ~]# echo ${path%%:*bin}
/usr/kerberos/sbin
# 同樣的, %% 代表的則是最長的符合字符串,所以結果其實是來自如下:
# /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:
# /usr/sbin:/usr/bin:/root/bin  <==這兩行其實是同一行啦!

由於我是想要由變量內容的後面向前面刪除,而我這個變量內容最後面的結尾是『/root/bin』, 所以你可以看到上面我刪除的數據最終一定是『bin』,亦即是『:*bin』那個 * 代表通配符! 至於 % 與 %% 的意義其實與 # 及 ## 類似!這樣理解否?

例題:
假設你是 root ,那你的 MAIL 變量應該是 /var/spool/mail/root 。假設你只想要保留最後面那個檔名 (root), 前面的目錄名稱都不要了,如何利用 $MAIL 變量來達成?
答:
題意其實是這樣『/var/spool/mail/root』,亦即刪除掉兩條斜線間的所有數據(最長符合)。 這個時候你就可以這樣做即可:
[root@www ~]# echo ${MAIL##//}
相反的,如果你只想要拿掉文件名,保留目錄的名稱,亦即是『/var/spool/mail/root』 (最短符合)。但假設你並不知道結尾的字母爲何,此時你可以利用通配符來處理即可,如下所示:
[root@www ~]# echo ${MAIL%/
}

瞭解了刪除功能後,接下來談談取代吧!繼續玩玩範例六囉!

範例六:將 path 的變量內容內的 sbin 取代成大寫 SBIN:
[root@www ~]# echo ${path/sbin/SBIN}
/usr/kerberos/SBIN:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:
/usr/sbin:/usr/bin:/root/bin
# 這個部分就容易理解的多了!關鍵詞在於那兩個斜線,兩斜線中間的是舊字符串
# 後面的是新字符串,所以結果就會出現如上述的特殊字體部分囉!

[root@www ~]# echo ${path//sbin/SBIN}
/usr/kerberos/SBIN:/usr/kerberos/bin:/usr/local/SBIN:/usr/local/bin:/SBIN:/bin:
/usr/SBIN:/usr/bin:/root/bin
# 如果是兩條斜線,那麼就變成所有符合的內容都會被取代喔!

我們將這部份作個總結說明一下:
在這裏插入圖片描述

2.8.2 變量的測試與內容替換

在某些時刻我們常常需要『判斷』某個變量是否存在,若變量存在則使用既有的配置,若變量不存在則給予一個常用的配置。 我們舉底下的例子來說明好了,看看能不能較容易被你所理解呢!

範例一:測試一下是否存在 username 這個變量,若不存在則給予 username 內容爲 root
[root@www ~]# echo $username
           <==由於出現空白,所以 username 可能不存在,也可能是空字符串
[root@www ~]# username=${username-root}
[root@www ~]# echo $username
root       <==因爲 username 沒有配置,所以主動給予名爲 root 的內容。
[root@www ~]# username="vbird tsai" <==主動配置 username 的內容
[root@www ~]# username=${username-root}
[root@www ~]# echo $username
vbird tsai <==因爲 username 已經配置了,所以使用舊有的配置而不以 root 取代

在上面的範例中,重點在於減號『 - 』後面接的關鍵詞!基本上你可以這樣理解:

new_var=${old_var-content}
   新的變量,主要用來取代舊變量。新舊變量名稱其實常常是一樣的

new_var=${old_var-content}
   這是本範例中的關鍵詞部分!必須要存在的哩!

new_var=${old_var-content}
   舊的變量,被測試的項目!

new_var=${old_var-content}
   變量的『內容』,在本範例中,這個部分是在『給予未配置變量的內容』

不過這還是有點問題!因爲 username 可能已經被配置爲『空字符串』了!果真如此的話,那你還可以使用底下的範例來給予 username 的內容成爲 root 喔!

範例二:若 username 未配置或爲空字符串,則將 username 內容配置爲 root
[root@www ~]# username=""
[root@www ~]# username=${username-root}
[root@www ~]# echo $username
      <==因爲 username 被配置爲空字符串了!所以當然還是保留爲空字符串!
[root@www ~]# username=${username:-root}
[root@www ~]# echo $username
root  <==加上『 : 』後若變量內容爲空或者是未配置,都能夠以後面的內容替換!

在大括號內有沒有冒號『 : 』的差別是很大的!加上冒號後,被測試的變量未被配置或者是已被配置爲空字符串時, 都能夠用後面的內容 (本例中是使用 root 爲內容) 來替換與配置!這樣可以瞭解了嗎?除了這樣的測試之外, 還有其他的測試方法喔!鳥哥將他整理如下:

Tips:
底下的例子當中,那個 var 與 str 爲變量,我們想要針對 str 是否有配置來決定 var 的值喔! 一般來說, str: 代表『str 沒配置或爲空的字符串時』;至於 str 則僅爲『沒有該變量』。

在這裏插入圖片描述
根據上面這張表,我們來進行幾個範例的練習吧! _!首先讓我們來測試一下,如果舊變量 (str) 不存在時, 我們要給予新變量一個內容,若舊變量存在則新變量內容以舊變量來替換,結果如下:

測試:先假設 str 不存在 (用 unset) ,然後測試一下減號 (-) 的用法:
[root@www ~]# unset str; var=${str-newvar}
[root@www ~]# echo var="$var", str="$str"
var=newvar, str=        <==因爲 str 不存在,所以 var 爲 newvar

測試:若 str 已存在,測試一下 var 會變怎樣?:
[root@www ~]# str="oldvar"; var=${str-newvar}
[root@www ~]# echo var="$var", str="$str"
var=oldvar, str=oldvar  <==因爲 str 存在,所以 var 等於 str 的內容

關於減號 (-) 其實上面我們談過了!這裏的測試只是要讓你更加了解,這個減號的測試並不會影響到舊變量的內容。 如果你想要將舊變量內容也一起替換掉的話,那麼就使用等號 (=) 吧!

測試:先假設 str 不存在 (用 unset) ,然後測試一下等號 (=) 的用法:
[root@www ~]# unset str; var=${str=newvar}
[root@www ~]# echo var="$var", str="$str"
var=newvar, str=newvar  <==因爲 str 不存在,所以 var/str 均爲 newvar

測試:如果 str 已存在了,測試一下 var 會變怎樣?
[root@www ~]# str="oldvar"; var=${str=newvar}
[root@www ~]# echo var="$var", str="$str"
var=oldvar, str=oldvar  <==因爲 str 存在,所以 var 等於 str 的內容

那如果我只是想知道,如果舊變量不存在時,整個測試就告知我『有錯誤』,此時就能夠使用問號『 ? 』的幫忙啦! 底下這個測試練習一下先!

測試:若 str 不存在時,則 var 的測試結果直接顯示 "無此變量"
[root@www ~]# unset str; var=${str?無此變量}
-bash: str: 無此變量    <==因爲 str 不存在,所以輸出錯誤信息 

測試:若 str 存在時,則 var 的內容會與 str 相同!
[root@www ~]# str="oldvar"; var=${str?novar}
[root@www ~]# echo var="$var", str="$str"
var=oldvar, str=oldvar  <==因爲 str 存在,所以 var 等於 str 的內容

基本上這種變量的測試也能夠透過 shell script 內的 if…then… 來處理, 不過既然 bash 有提供這麼簡單的方法來測試變量,那我們也可以多學一些嘛! 不過這種變量測試通常是在程序設計當中比較容易出現,如果這裏看不懂就先略過,未來有用到判斷變量值時,再回來看看吧! _

3. 命令別名與歷史命令

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

3.1 命令別名配置: alias, unalias

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

[root@www ~]# alias lm='ls -al | more'

立刻多出了一個可以運行的命令喔!這個命令名稱爲 lm ,且其實他是運行 ls -al | more 啊!真是方便。不過, 要注意的是:『alias 的定義守則與變量定義守則幾乎相同』, 所以你只要在 alias 後面加上你的 {『別名』=‘命令 選項…’ }, 以後你只要輸入 lm 就相當於輸入了 ls -al|more 這一串命令!很方便吧!

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

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

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

[root@www ~]# alias
alias cp='cp -i'
alias l.='ls -d .* --color=tty'
alias ll='ls -l --color=tty'
alias lm='ls -l | more'
alias ls='ls --color=tty'
alias mv='mv -i'
alias rm='rm -i'
alias which='alias | /usr/bin/which --tty-only --show-dot --show-tilde'

由上面的數據當中,你也會發現一件事情啊,我們在第十章的 vim 程序編輯器裏面提到 vi 與 vim 是不太一樣的,vim 可以多作一些額外的語法檢驗與顏色顯示,默認的 root 是單純使用 vi 而已。 如果你想要使用 vi 就直接以 vim 來開啓文件的話,使用『 alias vi=‘vim’ 』這個配置即可。 至於如果要取消命令別名的話,那麼就使用 unalias 吧!例如要將剛剛的 lm 命令別名拿掉,就使用:

[root@www ~]# unalias lm

那麼命令別名與變量有什麼不同呢?命令別名是『新創一個新的命令, 你可以直接下達該命令』的,至於變量則需要使用類似『 echo 』命令才能夠呼叫出變量的內容! 這兩者當然不一樣!很多初學者在這裏老是搞不清楚!要注意啊! _

例題:
DOS 年代,列出目錄與文件就是 dir ,而清除屏幕就是 cls ,那麼如果我想要在 linux 裏面也使用相同的命令呢?
答:
很簡單,透過 clear 與 ls 來進行命令別名的建置:
alias cls=‘clear’
alias dir=‘ls -l’

3.2 歷史命令: history, HISTSIZE

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

[root@www ~]# alias h='history'

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

[root@www ~]# history [n]
[root@www ~]# history [-c]
[root@www ~]# history [-raw] histfiles
選項與參數:
n   :數字,意思是『要列出最近的 n 筆命令行表』的意思!
-c  :將目前的 shell 中的所有 history 內容全部消除
-a  :將目前新增的 history 命令新增入 histfiles 中,若沒有加 histfiles ,
      則默認寫入 ~/.bash_history
-r  :將 histfiles 的內容讀到目前這個 shell 的 history 記憶中;
-w  :將目前的 history 記憶內容寫入 histfiles 中!

範例一:列出目前內存內的所有 history 記憶
[root@www ~]# history
# 前面省略
 1017  man bash
 1018  ll
 1019  history 
 1020  history
# 列出的信息當中,共分兩欄,第一欄爲該命令在這個 shell 當中的代碼,
# 另一個則是命令本身的內容喔!至於會秀出幾筆命令記錄,則與 HISTSIZE 有關!

範例二:列出目前最近的 3 筆數據
[root@www ~]# history 3
 1019  history 
 1020  history
 1021  history 3

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

在正常的情況下,歷史命令的讀取與記錄是這樣的:

  • 當我們以 bash 登陸 Linux 主機之後,系統會主動的由家目錄的 ~/.bash_history 讀取以前曾經下過的命令,那麼 ~/.bash_history 會記錄幾筆數據呢?這就與你 bash 的 HISTFILESIZE 這個變量配置值有關了!

  • 假設我這次登陸主機後,共下達過 100 次命令,『等我註銷時, 系統就會將 101~1100 這總共 1000 筆歷史命令升級到 ~/.bash_history 當中。』 也就是說,歷史命令在我註銷時,會將最近的 HISTFILESIZE 筆記錄到我的紀錄文件當中啦!

  • 當然,也可以用 history -w 強制立刻寫入的!那爲何用『升級』兩個字呢? 因爲 ~/.bash_history 記錄的筆數永遠都是 HISTFILESIZE 那麼多,舊的信息會被主動的拿掉! 僅保留最新的!
    那麼 history 這個歷史命令只可以讓我查詢命令而已嗎?呵呵!當然不止啊! 我們可以利用相關的功能來幫我們運行命令呢!舉例來說囉:

[root@www ~]# !number
[root@www ~]# !command
[root@www ~]# !!
選項與參數:
number  :運行第幾筆命令的意思;
command :由最近的命令向前搜尋『命令串開頭爲 command』的那個命令,並運行;
!!      :就是運行上一個命令(相當於按↑按鍵後,按 Enter)

[root@www ~]# history
   66  man rm
   67  alias
   68  man history
   69  history 
[root@www ~]# !66  <==運行第 66 筆命令
[root@www ~]# !!   <==運行上一個命令,本例中亦即 !66 
[root@www ~]# !al  <==運行最近以 al 爲開頭的命令(上頭列出的第 67 個)

經過上面的介紹,瞭乎?歷史命令用法可多了!如果我想要運行上一個命令, 除了使用上下鍵之外,我可以直接以『 !! 』 來下達上個命令的內容,此外, 我也可以直接選擇下達第 n 個命令,『 !n 』來運行,也可以使用命令標頭,例如 『 !vi 』來運行最近命令開頭是 vi 的命令列!相當的方便而好用!

基本上 history 的用途很大的!但是需要小心安全的問題!尤其是 root 的歷史紀錄文件,這是 Cracker 的最愛!因爲不小心的 root 會將很多的重要數據在運行的過程中會被紀錄在 ~/.bash_history 當中,如果這個文件被解析的話,後果不堪吶!無論如何,使用 history 配合『 ! 』曾經使用過的命令下達是很有效率的一個命令下達方法!

3.2.1 同一賬號同時多次登陸的 history 寫入問題

有些朋友在練習 linux 的時候喜歡同時開好幾個 bash 接口,這些 bash 的身份都是 root 。 這樣會有 ~/.bash_history 的寫入問題嗎?想一想,因爲這些 bash 在同時以 root 的身份登陸, 因此所有的 bash 都有自己的 1000 筆記錄在內存中。因爲等到註銷時纔會升級記錄文件,所以囉, 最後註銷的那個 bash 纔會是最後寫入的數據。唔!如此一來其他 bash 的命令操作就不會被記錄下來了 (其實有被記錄,只是被後來的最後一個 bash 所覆蓋升級了) 。

由於多重登陸有這樣的問題,所以很多朋友都習慣單一 bash 登陸,再用工作控制 (job control, 第四篇會介紹) 來切換不同工作! 這樣才能夠將所有曾經下達過的命令記錄下來,也才方便未來系統管理員進行命令的 debug 啊!

3.2.2 無法記錄時間

歷史命令還有一個問題,那就是無法記錄命令下達的時間。由於這 1000 筆歷史命令是依序記錄的, 但是並沒有記錄時間,所以在查詢方面會有一些不方便。如果讀者們有興趣,其實可以透過 ~/.bash_logout 來進行 history 的記錄,並加上 date 來添加時間參數,也是一個可以應用的方向喔! 有興趣的朋友可以先看看情境模擬題一吧!

4. Bash shell 的操作環境

是否記得我們登陸主機的時候,屏幕上頭會有一些說明文字,告知我們的 Linux 版本啊什麼的, 還有,登陸的時候我們還可以給予用戶一些信息或者歡迎文字呢。此外, 我們習慣的環境變量、命令別名等等的,是否可以登陸就主動的幫我配置好? 這些都是需要注意的。另外,這些配置值又可以分爲系統整體配置值與各人喜好配置值, 僅是一些文件放置的地點不同啦!這我們後面也會來談一談的!

4.1 路徑與命令搜尋順序

我們在第六章與第七章都曾談過『相對路徑與絕對路徑』的關係, 在本章的前幾小節也談到了 alias 與 bash 的內建命令。現在我們知道系統裏面其實有不少的 ls 命令, 或者是包括內建的 echo 命令,那麼來想一想,如果一個命令 (例如 ls) 被下達時, 到底是哪一個 ls 被拿來運行?很有趣吧!基本上,命令運行的順序可以這樣看:

  1. 以相對/絕對路徑運行命令,例如『 /bin/ls 』或『 ./ls 』;
  2. 由 alias 找到該命令來運行;
  3. 由 bash 內建的 (builtin) 命令來運行;
  4. 透過 $PATH 這個變量的順序搜尋到的第一個命令來運行。

舉例來說,你可以下達 /bin/ls 及單純的 ls 看看,會發現使用 ls 有顏色但是 /bin/ls 則沒有顏色。 因爲 /bin/ls 是直接取用該命令來下達,而 ls 會因爲『 alias ls=‘ls --color=tty’ 』這個命令別名而先使用! 如果想要了解命令搜尋的順序,其實透過 type -a ls 也可以查詢的到啦!上述的順序最好先了解喔!

例題:
配置 echo 的命令別名成爲 echo -n ,然後再觀察 echo 運行的順序
答:
[root@www ~]# alias echo=‘echo -n’
[root@www ~]# type -a echo
echo is aliased to `echo -n’
echo is a shell builtin
echo is /bin/echo
瞧!很清楚吧!先 alias 再 builtin 再由 $PATH 找到 /bin/echo 囉!

4.2 bash 的進站與歡迎信息: /etc/issue, /etc/motd

什麼! bash 也有進站畫面與歡迎信息喔?真假?真的啊! 還記得在終端機接口 (tty1 ~ tty6) 登陸的時候,會有幾行提示的字符串嗎?那就是進站畫面啊! 那個字符串寫在哪裏啊?呵呵!在 /etc/issue 裏面啊!先來看看:

[root@www ~]# cat /etc/issue
CentOS release 5.3 (Final)
Kernel \r on an \m

鳥哥是以完全未升級過的 CentOS 5.3 作爲範例,裏面默認有三行,較有趣的地方在於 \r 與 \m。 就如同 $PS1 這變量一樣,issue 這個文件的內容也是可以使用反斜槓作爲變量取用喔!你可以 man issue 配合 man mingetty 得到底下的結果:

issue 內的各代碼意義

  • \d 本地端時間的日期;
  • \l 顯示第幾個終端機接口;
  • \m 顯示硬件的等級 (i386/i486/i586/i686…);
  • \n 顯示主機的網絡名稱;
  • \o 顯示 domain name;
  • \r 操作系統的版本 (相當於 uname -r)
  • \t 顯示本地端時間的時間;
  • \s 操作系統的名稱;
  • \v 操作系統的版本。

做一下底下這個練習,看看能不能取得你要的進站畫面?

例題:
如果你在 tty3 的進站畫面看到如下顯示,該如何配置才能得到如下畫面?
CentOS release 5.3 (Final) (terminal: tty3)
Date: 2009-02-05 17:29:19
Kernel 2.6.18-128.el5 on an i686
Welcome!
注意,tty3 在不同的 tty 有不同顯示,日期則是再按下 [enter] 後就會所有不同。
答:
很簡單,參考上述的反斜槓功能去修改 /etc/issue 成爲如下模樣即可(共五行):
CentOS release 5.3 (Final) (terminal: \l)
Date: \d \t
Kernel \r on an \m
Welcome!
曾有鳥哥的學生在這個 /etc/issue 內修改數據,光是利用簡單的英文字母作出屬於他自己的進站畫面, 畫面裏面有他的中文名字呢!非常厲害!也有學生做成類似很大一個『囧』在進站畫面,都非常有趣!

你要注意的是,除了 /etc/issue 之外還有個 /etc/issue.net 呢!這是啥?這個是提供給 telnet 這個遠程登錄程序用的。 當我們使用 telnet 連接到主機時,主機的登陸畫面就會顯示 /etc/issue.net 而不是 /etc/issue 呢!

至於如果您想要讓使用者登陸後取得一些信息,例如您想要讓大家都知道的信息, 那麼可以將信息加入 /etc/motd 裏面去!例如:當登陸後,告訴登陸者, 系統將會在某個固定時間進行維護工作,可以這樣做:

[root@www ~]# vi /etc/motd
Hello everyone,
Our server will be maintained at 2009/02/28 0:00 ~ 24:00.
Please don't login server at that time. ^_^

那麼當你的使用者(包括所有的一般賬號與 root)登陸主機後,就會顯示這樣的信息出來:

Last login: Thu Feb  5 22:35:47 2009 from 127.0.0.1
Hello everyone,
Our server will be maintained at 2009/02/28 0:00 ~ 24:00.
Please don't login server at that time. ^_^

4.3 環境配置文件: login, non-login shell, /etc/profile, ~/.bash_profile, source, ~/.bashrc

你是否會覺得奇怪,怎麼我們什麼動作都沒有進行,但是一進入 bash 就取得一堆有用的變量了? 這是因爲系統有一些環境配置文件案的存在,讓 bash 在啓動時直接讀取這些配置文件,以規劃好 bash 的操作環境啦! 而這些配置文件又可以分爲全體系統的配置文件以及用戶個人偏好配置文件。要注意的是, 我們前幾個小節談到的命令別名啦、自定義的變量啦,在你註銷 bash 後就會失效,所以你想要保留你的配置, 就得要將這些配置寫入配置文件纔行。底下就讓我們來聊聊吧!

4.3.1 login 與 non-login shell

在開始介紹 bash 的配置文件前,我們一定要先知道的就是 login shell 與 non-login shell! 重點在於有沒有登陸 (login) 啦!

  • login shell:取得 bash 時需要完整的登陸流程的,就稱爲 login shell。舉例來說,你要由 tty1 ~ tty6 登陸,需要輸入用戶的賬號與密碼,此時取得的 bash 就稱爲『 login shell 』囉;

  • non-login shell:取得 bash 接口的方法不需要重複登陸的舉動,舉例來說,(1)你以 X window 登陸 Linux 後, 再以 X 的圖形化接口啓動終端機,此時那個終端接口並沒有需要再次的輸入賬號與密碼,那個 bash 的環境就稱爲 non-login shell了。(2)你在原本的 bash 環境下再次下達 bash 這個命令,同樣的也沒有輸入賬號密碼, 那第二個 bash (子程序) 也是 non-login shell 。

爲什麼要介紹 login, non-login shell 呢?這是因爲這兩個取得 bash 的情況中,讀取的配置文件數據並不一樣所致。 由於我們需要登陸系統,所以先談談 login shell 會讀取哪些配置文件?一般來說,login shell 其實只會讀取這兩個配置文件:

  1. /etc/profile:這是系統整體的配置,你最好不要修改這個文件;
  2. ~/.bash_profile 或 ~/.bash_login 或 ~/.profile:屬於使用者個人配置,你要改自己的數據,就寫入這裏!

那麼,就讓我們來聊一聊這兩個文件吧!這兩個文件的內容可是非常繁複的喔!

4.3.2 /etc/profile (login shell 纔會讀)

你可以使用 vim 去閱讀一下這個文件的內容。這個配置文件可以利用使用者的標識符 (UID) 來決定很多重要的變量數據, 這也是每個使用者登陸取得 bash 時一定會讀取的配置文件! 所以如果你想要幫所有使用者配置整體環境,那就是改這裏囉!不過,沒事還是不要隨便改這個文件喔 這個文件配置的變量主要有:

  • PATH:會依據 UID 決定 PATH 變量要不要含有 sbin 的系統命令目錄;
  • MAIL:依據賬號配置好使用者的 mailbox 到 /var/spool/mail/賬號名;
  • USER:根據用戶的賬號配置此一變量內容;
  • HOSTNAME:依據主機的 hostname 命令決定此一變量內容;
  • HISTSIZE:歷史命令記錄筆數。CentOS 5.x 配置爲 1000 ;

/etc/profile 可不止會做這些事而已,他還會去呼叫外部的配置數據喔!在 CentOS 5.x 默認的情況下, 底下這些數據會依序的被呼叫進來:

/etc/inputrc

其實這個文件並沒有被運行啦!/etc/profile 會主動的判斷使用者有沒有自定義輸入的按鍵功能,如果沒有的話, /etc/profile 就會決定配置『INPUTRC=/etc/inputrc』這個變量!此一文件內容爲 bash 的熱鍵啦、[tab]要不要有聲音啦等等的數據! 因爲鳥哥覺得 bash 默認的環境已經很棒了,所以不建議修改這個文件!

/etc/profile.d/*.sh

其實這是個目錄內的衆多文件!只要在 /etc/profile.d/ 這個目錄內且擴展名爲 .sh ,另外,使用者能夠具有 r 的權限, 那麼該文件就會被 /etc/profile 呼叫進來。在 CentOS 5.x 中,這個目錄底下的文件規範了 bash 操作接口的顏色、 語系、ll 與 ls 命令的命令別名、vi 的命令別名、which 的命令別名等等。如果你需要幫所有使用者配置一些共享的命令別名時, 可以在這個目錄底下自行創建擴展名爲 .sh 的文件,並將所需要的數據寫入即可喔!

/etc/sysconfig/i18n

這個文件是由 /etc/profile.d/lang.sh 呼叫進來的!這也是我們決定 bash 默認使用何種語系的重要配置文件! 文件裏最重要的就是 LANG 這個變量的配置啦!我們在前面的 locale 討論過這個文件囉! 自行回去瞧瞧先!

反正你只要記得,bash 的 login shell 情況下所讀取的整體環境配置文件其實只有 /etc/profile,但是 /etc/profile 還會呼叫出其他的配置文件,所以讓我們的 bash 操作接口變的非常的友善啦! 接下來,讓我們來瞧瞧,那麼個人偏好的配置文件又是怎麼回事?

4.3.3 ~/.bash_profile (login shell 纔會讀)

bash 在讀完了整體環境配置的 /etc/profile 並藉此呼叫其他配置文件後,接下來則是會讀取使用者的個人配置文件。 在 login shell 的 bash 環境中,所讀取的個人偏好配置文件其實主要有三個,依序分別是:

  1. ~/.bash_profile
  2. ~/.bash_login
  3. ~/.profile

其實 bash 的 login shell 配置只會讀取上面三個文件的其中一個, 而讀取的順序則是依照上面的順序。也就是說,如果 ~/.bash_profile 存在,那麼其他兩個文件不論有無存在,都不會被讀取。 如果 ~/.bash_profile 不存在纔會去讀取 ~/.bash_login,而前兩者都不存在纔會讀取 ~/.profile 的意思。 會有這麼多的文件,其實是因應其他 shell 轉換過來的使用者的習慣而已。 先讓我們來看一下 root 的 /root/.bash_profile 的內容是怎樣呢?

[root@www ~]# cat ~/.bash_profile
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then   <==底下這三行在判斷並讀取 ~/.bashrc
        . ~/.bashrc
fi

# User specific environment and startup programs
PATH=$PATH:$HOME/bin        <==底下這幾行在處理個人化配置
export PATH
unset USERNAME

這個文件內有配置 PATH 這個變量喔!而且還使用了 export 將 PATH 變成環境變量呢! 由於 PATH 在 /etc/profile 當中已經配置過,所以在這裏就以累加的方式添加用戶家目錄下的 ~/bin/ 爲額外的運行文件放置目錄。這也就是說,你可以將自己創建的運行檔放置到你自己家目錄下的 ~/bin/ 目錄啦! 那就可以直接運行該運行檔而不需要使用絕對/相對路徑來運行該文件。

這個文件的內容比較有趣的地方在於 if … then … 那一段!那一段程序代碼我們會在第十三章 shell script 談到,假設你現在是看不懂的。 該段的內容指的是『判斷家目錄下的 ~/.bashrc 存在否,若存在則讀入 ~/.bashrc 的配置』。 bash 配置文件的讀入方式比較有趣,主要是透過一個命令『 source 』來讀取的! 也就是說 ~/.bash_profile 其實會再呼叫 ~/.bashrc 的配置內容喔!最後,我們來看看整個 login shell 的讀取流程:
在這裏插入圖片描述

4.3.4 source :讀入環境配置文件的命令

由於 /etc/profile 與 ~/.bash_profile 都是在取得 login shell 的時候纔會讀取的配置文件,所以, 如果你將自己的偏好配置寫入上述的文件後,通常都是得註銷再登陸後,該配置纔會生效。那麼,能不能直接讀取配置文件而不註銷登陸呢? 可以的!那就得要利用 source 這個命令了!

[root@www ~]# source 配置文件檔名

範例:將家目錄的 ~/.bashrc 的配置讀入目前的 bash 環境中
[root@www ~]# source ~/.bashrc <==底下這兩個命令是一樣的!
[root@www ~]# . ~/.bashrc
利用 source 或小數點 (.) 都可以將配置文件的內容讀進來目前的 shell 環境中! 舉例來說,我修改了 ~/.bashrc ,那麼不需要註銷,立即以 source ~/.bashrc 就可以將剛剛最新配置的內容讀進來目前的環境中!很不錯吧!還有,包括 ~/bash_profile 以及 /etc/profile 的配置中, 很多時候也都是利用到這個 source (或小數點) 的功能喔!

有沒有可能會使用到不同環境配置文件的時候?有啊! 最常發生在一個人的工作環境分爲多種情況的時候了!舉個例子來說,在鳥哥的大型主機中, 常常需要負責兩到三個不同的案子,每個案子所需要處理的環境變量訂定並不相同, 那麼鳥哥就將這兩三個案子分別編寫屬於該案子的環境變量配置文件案,當需要該環境時,就直接『 source 變量文件 』,如此一來,環境變量的配置就變的更簡便而靈活了!

4.3.5 ~/.bashrc (non-login shell 會讀)

談完了 login shell 後,那麼 non-login shell 這種非登陸情況取得 bash 操作接口的環境配置文件又是什麼? 當你取得 non-login shell 時,該 bash 配置文件僅會讀取 ~/.bashrc 而已啦!那麼默認的 ~/.bashrc 內容是如何?

[root@www ~]# cat ~/.bashrc
# .bashrc

# User specific aliases and functions
alias rm='rm -i'             <==使用者的個人配置
alias cp='cp -i'
alias mv='mv -i'

# Source global definitions
if [ -f /etc/bashrc ]; then  <==整體的環境配置
        . /etc/bashrc
fi

特別注意一下,由於 root 的身份與一般使用者不同,鳥哥是以 root 的身份取得上述的數據, 如果是一般使用者的 ~/.bashrc 會有些許不同。看一下,你會發現在 root 的 ~/.bashrc 中其實已經規範了較爲保險的命令別名了。 此外,咱們的 CentOS 5.x 還會主動的呼叫 /etc/bashrc 這個文件喔!爲什麼需要呼叫 /etc/bashrc 呢? 因爲 /etc/bashrc 幫我們的 bash 定義出底下的數據:

  • 依據不同的 UID 規範出 umask 的值;
  • 依據不同的 UID 規範出提示字符 (就是 PS1 變量);
  • 呼叫 /etc/profile.d/*.sh 的配置

你要注意的是,這個 /etc/bashrc 是 CentOS 特有的 (其實是 Red Hat 系統特有的),其他不同的 distributions 可能會放置在不同的檔名就是了。由於這個 ~/.bashrc 會呼叫 /etc/bashrc 及 /etc/profile.d/*.sh , 所以,萬一你沒有 ~/.bashrc (可能自己不小心將他刪除了),那麼你會發現你的 bash 提示字符可能會變成這個樣子:

-bash-3.2$  

不要太擔心啦!這是正常的,因爲你並沒有呼叫 /etc/bashrc 來規範 PS1 變量啦!而且這樣的情況也不會影響你的 bash 使用。 如果你想要將命令提示字符捉回來,那麼可以複製 /etc/skel/.bashrc 到你的家目錄,再修訂一下你所想要的內容, 並使用 source 去呼叫 ~/.bashrc ,那你的命令提示字符就會回來啦!

4.3.6 其他相關配置文件

事實上還有一些配置文件可能會影響到你的 bash 操作的,底下就來談一談:

/etc/man.config

這個文件乍看之下好像跟 bash 沒相關性,但是對於系統管理員來說, 卻也是很重要的一個文件!這的文件的內容『規範了使用 man 的時候, man page 的路徑到哪裏去尋找!』所以說的簡單一點,這個文件規定了下達 man 的時候,該去哪裏查看數據的路徑配置!

那麼什麼時候要來修改這個文件呢?如果你是以 tarball 的方式來安裝你的數據,那麼你的 man page 可能會放置在 /usr/local/softpackage/man 裏頭,那個 softpackage 是你的套件名稱, 這個時候你就得以手動的方式將該路徑加到 /etc/man.config 裏頭,否則使用 man 的時候就會找不到相關的說明檔囉。

事實上,這個文件內最重要的其實是 MANPATH 這個變量配置啦! 我們搜尋 man page 時,會依據 MANPATH 的路徑去分別搜尋啊!另外,要注意的是, 這個文件在各大不同版本 Linux distributions 中,檔名都不太相同,例如 CentOS 用的是 /etc/man.config ,而 SuSE 用的則是 /etc/manpath.config , 可以利用 [tab] 按鍵來進行文件名的補齊啦!

~/.bash_history

還記得我們在歷史命令提到過這個文件吧?默認的情況下, 我們的歷史命令就記錄在這裏啊!而這個文件能夠記錄幾筆數據,則與 HISTFILESIZE 這個變量有關啊。每次登陸 bash 後,bash 會先讀取這個文件,將所有的歷史命令讀入內存, 因此,當我們登陸 bash 後就可以查知上次使用過哪些命令囉。至於更多的歷史命令, 請自行回去參考喔!

~/.bash_logout

這個文件則記錄了『當我註銷 bash 後,系統再幫我做完什麼動作後才離開』的意思。 你可以去讀取一下這個文件的內容,默認的情況下,註銷時, bash 只是幫我們清掉屏幕的信息而已。 不過,你也可以將一些備份或者是其他你認爲重要的工作寫在這個文件中 (例如清空緩存盤), 那麼當你離開 Linux 的時候,就可以解決一些煩人的事情囉!

4.4 終端機的環境配置: stty, set

我們在第五章首次登陸 Linux 時就提過,可以在 tty1 ~ tty6 這六個文字接口的終端機 (terminal) 環境中登陸,登陸的時候我們可以取得一些字符配置的功能喔! 舉例來說,我們可以利用退格鍵 (backspace,就是那個←符號的按鍵) 來刪除命令行上的字符, 也可以使用 [ctrl]+c 來強制終止一個命令的運行,當輸入錯誤時,就會有聲音跑出來警告。這是怎麼辦到的呢? 很簡單啊!因爲登陸終端機的時候,會自動的取得一些終端機的輸入環境的配置啊!

事實上,目前我們使用的 Linux distributions 都幫我們作了最棒的使用者環境了, 所以大家可以不用擔心操作環境的問題。不過,在某些 Unix like 的機器中,還是可能需要動用一些手腳, 才能夠讓我們的輸入比較快樂~舉例來說,利用 [backspace] 刪除,要比利用 [Del] 按鍵來的順手吧! 但是某些 Unix 偏偏是以 [del] 來進行字符的刪除啊!所以,這個時候就可以動動手腳囉~

那麼如何查閱目前的一些按鍵內容呢?可以利用 stty (setting tty 終端機的意思) 呢! stty 也可以幫助配置終端機的輸入按鍵代表意義喔!

[root@www ~]# stty [-a]
選項與參數:
-a  :將目前所有的 stty 參數列出來;

範例一:列出所有的按鍵與按鍵內容
[root@www ~]# stty -a
speed 38400 baud; rows 24; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; 
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z;
rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
....(以下省略)....

我們可以利用 stty -a 來列出目前環境中所有的按鍵列表,在上頭的列表當中,需要注意的是特殊字體那幾個, 此外,如果出現 ^ 表示 [Ctrl] 那個按鍵的意思。舉例來說, intr = ^C 表示利用 [ctrl] + c 來達成的。幾個重要的代表意義是:

  • eof : End of file 的意思,代表『結束輸入』。
  • erase : 向後刪除字符,
  • intr : 送出一個 interrupt (中斷) 的訊號給目前正在 run 的程序;
  • kill : 刪除在目前命令列上的所有文字;
  • quit : 送出一個 quit 的訊號給目前正在 run 的程序;
  • start : 在某個程序停止後,重新啓動他的 output
  • stop : 停止目前屏幕的輸出;
  • susp : 送出一個 terminal stop 的訊號給正在 run 的程序。

記不記得我們在第五章講過幾個 Linux 熱鍵啊?沒錯! 就是這個 stty 配置值內的 intr / eof 囉~至於刪除字符,就是 erase 那個配置值啦! 如果你想要用 [ctrl]+h 來進行字符的刪除,那麼可以下達:

[root@www ~]# stty erase ^h

那麼從此之後,你的刪除字符就得要使用 [ctrl]+h 囉,按下 [backspace] 則會出現 ^? 字樣呢! 如果想要回複利用 [backspace] ,就下達 stty erase ^? 即可啊! 至於更多的 stty 說明,記得參考一下 man stty 的內容喔!

除了 stty 之外,其實我們的 bash 還有自己的一些終端機配置值呢!那就是利用 set 來配置的! 我們之前提到一些變量時,可以利用 set 來顯示,除此之外,其實 set 還可以幫我們配置整個命令輸出/輸入的環境。 例如記錄歷史命令、顯示錯誤內容等等。

[root@www ~]# set [-uvCHhmBx]
選項與參數:
-u  :默認不激活。若激活後,當使用未配置變量時,會顯示錯誤信息;
-v  :默認不激活。若激活後,在信息被輸出前,會先顯示信息的原始內容;
-x  :默認不激活。若激活後,在命令被運行前,會顯示命令內容(前面有 ++ 符號)
-h  :默認激活。與歷史命令有關;
-H  :默認激活。與歷史命令有關;
-m  :默認激活。與工作管理有關;
-B  :默認激活。與刮號 [] 的作用有關;
-C  :默認不激活。若使用 > 等,則若文件存在時,該文件不會被覆蓋。

範例一:顯示目前所有的 set 配置值
[root@www ~]# echo $-
himBH
# 那個 $- 變量內容就是 set 的所有配置啦! bash 默認是 himBH 喔!

範例二:配置 "若使用未定義變量時,則顯示錯誤信息" 
[root@www ~]# set -u
[root@www ~]# echo $vbirding
-bash: vbirding: unbound variable
# 默認情況下,未配置/未宣告 的變量都會是『空的』,不過,若配置 -u 參數,
# 那麼當使用未配置的變量時,就會有問題啦!很多的 shell 都默認激活 -u 參數。
# 若要取消這個參數,輸入 set +u 即可!

範例三:運行前,顯示該命令內容。
[root@www ~]# set -x
[root@www ~]# echo $HOME
+ echo /root
/root
++ echo -ne '\033]0;root@www:~'
# 看見否?要輸出的命令都會先被打印到屏幕上喔!前面會多出 + 的符號!

另外,其實我們還有其他的按鍵配置功能呢!就是在前一小節提到的 /etc/inputrc 這個文件裏面配置。

[root@www ~]# cat /etc/inputrc
# do not bell on tab-completion
#set bell-style none

set meta-flag on
set input-meta on
set convert-meta off
set output-meta on
.....以下省略.....

還有例如 /etc/DIR_COLORS* 與 /etc/termcap 等,也都是與終端機有關的環境配置文件案呢! 不過,事實上,鳥哥並不建議您修改 tty 的環境呢,這是因爲 bash 的環境已經配置的很親和了, 我們不需要額外的配置或者修改,否則反而會產生一些困擾。不過,寫在這裏的數據, 只是希望大家能夠清楚的知道我們的終端機是如何進行配置的喔! _! 最後,我們將 bash 默認的組合鍵給他彙整如下:
在這裏插入圖片描述

4.5 通配符與特殊符號

在 bash 的操作環境中還有一個非常有用的功能,那就是通配符 (wildcard) ! 我們利用 bash 處理數據就更方便了!底下我們列出一些常用的通配符喔:
在這裏插入圖片描述
接下來讓我們利用通配符來玩些東西吧!首先,利用通配符配合 ls 找檔名看看:

[root@www ~]# LANG=C              <==由於與編碼有關,先配置語系一下

範例一:找出 /etc/ 底下以 cron 爲開頭的檔名
[root@www ~]# ll -d /etc/cron*    <==加上 -d 是爲了僅顯示目錄而已

範例二:找出 /etc/ 底下文件名『剛好是五個字母』的文件名
[root@www ~]# ll -d /etc/?????    <==由於 ? 一定有一個,所以五個 ? 就對了

範例三:找出 /etc/ 底下文件名含有數字的文件名
[root@www ~]# ll -d /etc/*[0-9]*  <==記得中括號左右兩邊均需 *

範例四:找出 /etc/ 底下,檔名開頭非爲小寫字母的文件名:
[root@www ~]# ll -d /etc/[^a-z]*  <==注意中括號左邊沒有 *

範例五:將範例四找到的文件複製到 /tmp 中
[root@www ~]# cp -a /etc/[^a-z]* /tmp

除了通配符之外,bash 環境中的特殊符號有哪些呢?底下我們先彙整一下:
在這裏插入圖片描述
以上爲 bash 環境中常見的特殊符號彙整!理論上,你的『檔名』儘量不要使用到上述的字符啦!

5. 數據流重導向 (Redirection)

數據流重導向 (redirect) 由字面上的意思來看,好像就是將『數據給他傳導到其他地方去』的樣子? 沒錯~數據流重導向就是將某個命令運行後應該要出現在屏幕上的數據, 給他傳輸到其他的地方,例如文件或者是裝置 (例如打印機之類的)!這玩意兒在 Linux 的文本模式底下可重要的! 尤其是如果我們想要將某些數據儲存下來時,就更有用了!

5.1 何謂數據流重導向?

什麼是數據流重導向啊?這得要由命令的運行結果談起!一般來說,如果你要運行一個命令,通常他會是這樣的:
在這裏插入圖片描述
我們運行一個命令的時候,這個命令可能會由文件讀入數據,經過處理之後,再將數據輸出到屏幕上。 在上圖當中, standard output 與 standard error output 分別代表『標準輸出』與『標準錯誤輸出』, 這兩個玩意兒默認都是輸出到屏幕上面來的啊!那麼什麼是標準輸出與標準錯誤輸出呢?

5.1.1 standard output 與 standard error output

簡單的說,標準輸出指的是『命令運行所回傳的正確的信息』,而標準錯誤輸出可理解爲『 命令運行失敗後,所回傳的錯誤信息』。舉個簡單例子來說,我們的系統默認有 /etc/crontab 但卻無 /etc/vbirdsay, 此時若下達『 cat /etc/crontab /etc/vbirdsay 』這個命令時,cat 會進行:

  • 標準輸出:讀取 /etc/crontab 後,將該文件內容顯示到屏幕上;
  • 標準錯誤輸出:因爲無法找到 /etc/vbirdsay,因此在屏幕上顯示錯誤信息

不管正確或錯誤的數據都是默認輸出到屏幕上,所以屏幕當然是亂亂的!那能不能透過某些機制將這兩股數據分開呢? 當然可以啊!那就是數據流重導向的功能啊!數據流重導向可以將 standard output (簡稱 stdout) 與 standard error output (簡稱 stderr) 分別傳送到其他的文件或裝置去,而分別傳送所用的特殊字符則如下所示:

  • 標準輸入  (stdin) :代碼爲 0 ,使用 < 或 << ;
  • 標準輸出  (stdout):代碼爲 1 ,使用 > 或 >> ;
  • 標準錯誤輸出(stderr):代碼爲 2 ,使用 2> 或 2>> ;
    爲了理解 stdout 與 stderr ,我們先來進行一個範例的練習:
範例一:觀察你的系統根目錄 (/) 下各目錄的文件名、權限與屬性,並記錄下來
[root@www ~]# ll /  <==此時屏幕會顯示出文件名信息

[root@www ~]# ll / > ~/rootfile <==屏幕並無任何信息
[root@www ~]# ll  ~/rootfile <==有個新檔被創建了!
-rw-r--r-- 1 root root 1089 Feb  6 17:00 /root/rootfile

怪了!屏幕怎麼會完全沒有數據呢?這是因爲原本『 ll / 』所顯示的數據已經被重新導向到 ~/rootfile 文件中了! 那個 ~/rootfile 的檔名可以隨便你取。如果你下達『 cat ~/rootfile 』那就可以看到原本應該在屏幕上面的數據囉。 如果我再次下達:『 ll /home > ~/rootfile 』後,那個 ~/rootfile 文件的內容變成什麼? 他將變成『僅有 ll /home 的數據』而已!咦!原本的『 ll / 』數據就不見了嗎?是的!因爲該文件的創建方式是:

  1. 該文件 (本例中是 ~/rootfile) 若不存在,系統會自動的將他創建起來,但是
  2. 當這個文件存在的時候,那麼系統就會先將這個文件內容清空,然後再將數據寫入!
  3. 也就是若以 > 輸出到一個已存在的文件中,那個文件就會被覆蓋掉囉!

那如果我想要將數據累加而不想要將舊的數據刪除,那該如何是好?利用兩個大於的符號 (>>) 就好啦!以上面的範例來說,你應該要改成『 ll / >> ~/rootfile 』即可。 如此一來,當 (1) ~/rootfile 不存在時系統會主動創建這個文件;(2)若該文件已存在, 則數據會在該文件的最下方累加進去!

上面談到的是 standard output 的正確數據,那如果是 standard error output 的錯誤數據呢?那就透過 2> 及 2>> 囉!同樣是覆蓋 (2>) 與累加 (2>>) 的特性!我們在剛剛纔談到 stdout 代碼是 1 而 stderr 代碼是 2 , 所以這個 2> 是很容易理解的,而如果僅存在 > 時,則代表默認的代碼 1 囉!也就是說:

  • 1> :以覆蓋的方法將『正確的數據』輸出到指定的文件或裝置上;
  • 1>>:以累加的方法將『正確的數據』輸出到指定的文件或裝置上;
  • 2> :以覆蓋的方法將『錯誤的數據』輸出到指定的文件或裝置上;
  • 2>>:以累加的方法將『錯誤的數據』輸出到指定的文件或裝置上;

要注意喔,『 1>> 』以及『 2>> 』中間是沒有空格的!OK!有些概念之後讓我們繼續聊一聊這傢伙怎麼應用吧! 當你以一般身份運行 find 這個命令的時候,由於權限的問題可能會產生一些錯誤信息。例如運行『 find / -name testing 』時,可能會產生類似『 find: /root: Permission denied 』之類的信息。 例如底下這個範例:

範例二:利用一般身份賬號搜尋 /home 底下是否有名爲 .bashrc 的文件存在
[root@www ~]# su - dmtsai  <==假設我的系統有名爲 dmtsai 的賬號
[dmtsai@www ~]$ find /home -name .bashrc <==身份是 dmtsai 喔!
find: /home/lost+found: Permission denied  <== Standard error
find: /home/alex: Permission denied        <== Standard error
find: /home/arod: Permission denied        <== Standard error
/home/dmtsai/.bashrc                       <== Standard output

由於 /home 底下還有我們之前創建的賬號存在,那些賬號的家目錄你當然不能進入啊!所以就會有錯誤及正確數據了。 好了,那麼假如我想要將數據輸出到 list 這個文件中呢?運行『 find /home -name .bashrc > list 』 會有什麼結果?呵呵,你會發現 list 裏面存了剛剛那個『正確』的輸出數據, 至於屏幕上還是會有錯誤的信息出現呢!傷腦筋!如果想要將正確的與錯誤的數據分別存入不同的文件中需要怎麼做?

範例三:承範例二,將 stdout 與 stderr 分存到不同的文件去
[dmtsai@www ~]$ find /home -name .bashrc > list_right 2> list_error

注意喔,此時『屏幕上不會出現任何信息』!因爲剛剛運行的結果中,有 Permission 的那幾行錯誤信息都會跑到 list_error 這個文件中,至於正確的輸出數據則會存到 list_right 這個文件中囉!這樣可以瞭解了嗎? 如果有點混亂的話,去休息一下再來看看吧!

5.1.2 /dev/null 垃圾桶黑洞裝置與特殊寫法

想象一下,如果我知道錯誤信息會發生,所以要將錯誤信息忽略掉而不顯示或儲存呢? 這個時候黑洞裝置 /dev/null 就很重要了!這個 /dev/null 可以喫掉任何導向這個裝置的信息喔!將上述的範例修訂一下:

範例四:承範例三,將錯誤的數據丟棄,屏幕上顯示正確的數據
[dmtsai@www ~]$ find /home -name .bashrc 2> /dev/null
/home/dmtsai/.bashrc  <==只有 stdout 會顯示到屏幕上, stderr 被丟棄了

再想象一下,如果我要將正確與錯誤數據通通寫入同一個文件去呢?這個時候就得要使用特殊的寫法了! 我們同樣用底下的案例來說明:

範例五:將命令的數據全部寫入名爲 list 的文件中
[dmtsai@www ~]$ find /home -name .bashrc > list 2> list  <==錯誤
[dmtsai@www ~]$ find /home -name .bashrc > list 2>&1     <==正確
[dmtsai@www ~]$ find /home -name .bashrc &> list         <==正確

上述表格第一行錯誤的原因是,由於兩股數據同時寫入一個文件,又沒有使用特殊的語法, 此時兩股數據可能會交叉寫入該文件內,造成次序的錯亂。所以雖然最終 list 文件還是會產生,但是裏面的數據排列就會怪怪的,而不是原本屏幕上的輸出排序。 至於寫入同一個文件的特殊語法如上表所示,你可以使用 2>&1 也可以使用 &> ! 一般來說,鳥哥比較習慣使用 2>&1 的語法啦!

5.1.3 standard input : < 與 <<

瞭解了 stderr 與 stdout 後,那麼那個 < 又是什麼呀?呵呵!以最簡單的說法來說, 那就是『將原本需要由鍵盤輸入的數據,改由文件內容來取代』的意思。 我們先由底下的 cat 命令操作來了解一下什麼叫做『鍵盤輸入』吧!

範例六:利用 cat 命令來創建一個文件的簡單流程
[root@www ~]# cat > catfile
testing
cat file test
<==這裏按下 [ctrl]+d 來離開

[root@www ~]# cat catfile
testing
cat file test

由於加入 > 在 cat 後,所以那個 catfile 會被主動的創建,而內容就是剛剛鍵盤上面輸入的那兩行數據了。 唔!那我能不能用純文本文件取代鍵盤的輸入,也就是說,用某個文件的內容來取代鍵盤的敲擊呢? 可以的!如下所示:

範例七:用 stdin 取代鍵盤的輸入以創建新文件的簡單流程
[root@www ~]# cat > catfile < ~/.bashrc
[root@www ~]# ll catfile ~/.bashrc
-rw-r--r-- 1 root root 194 Sep 26 13:36 /root/.bashrc
-rw-r--r-- 1 root root 194 Feb  6 18:29 catfile
# 注意看,這兩個文件的大小會一模一樣!幾乎像是使用 cp 來複制一般!

這東西非常的有幫助!尤其是用在類似 mail 這種命令的使用上。 理解 < 之後,再來則是怪可怕一把的 << 這個連續兩個小於的符號了。 他代表的是『結束的輸入字符』的意思!舉例來講:『我要用 cat 直接將輸入的信息輸出到 catfile 中, 且當由鍵盤輸入 eof 時,該次輸入就結束』,那我可以這樣做:

[root@www ~]# cat > catfile << "eof"
> This is a test.
> OK now stop
> eof  <==輸入這關鍵詞,立刻就結束而不需要輸入 [ctrl]+d

[root@www ~]# cat catfile
This is a test.
OK now stop     <==只有這兩行,不會存在關鍵詞那一行!

看到了嗎?利用 << 右側的控制字符,我們可以終止一次輸入, 而不必輸入 [crtl]+d 來結束哩!這對程序寫作很有幫助喔!好了,那麼爲何要使用命令輸出重導向呢?我們來說一說吧!

  • 屏幕輸出的信息很重要,而且我們需要將他存下來的時候;
  • 背景運行中的程序,不希望他干擾屏幕正常的輸出結果時;
  • 一些系統的例行命令 (例如寫在 /etc/crontab 中的文件) 的運行結果,希望他可以存下來時;
  • 一些運行命令的可能已知錯誤信息時,想以『 2> /dev/null 』將他丟掉時;
  • 錯誤信息與正確信息需要分別輸出時。

當然還有很多的功能的,最簡單的就是網友們常常問到的:『爲何我的 root 都會收到系統 crontab 寄來的錯誤信息呢』這個咚咚是常見的錯誤, 而如果我們已經知道這個錯誤信息是可以忽略的時候,嗯!『 2> errorfile 』這個功能就很重要了吧! 瞭解了嗎?

5.2 命令運行的判斷依據: ; , &&, ||

在某些情況下,很多命令我想要一次輸入去運行,而不想要分次運行時,該如何是好?基本上你有兩個選擇, 一個是透過第十三章要介紹的 shell script 撰寫腳本去運行,一種則是透過底下的介紹來一次輸入多重命令喔!

5.2.1 cmd ; cmd (不考慮命令相關性的連續命令下達)

在某些時候,我們希望可以一次運行多個命令,例如在關機的時候我希望可以先運行兩次 sync 同步化寫入磁盤後才 shutdown 計算機,那麼可以怎麼作呢?這樣做呀:

[root@www ~]# sync; sync; shutdown -h now

在命令與命令中間利用分號 (😉 來隔開,這樣一來,分號前的命令運行完後就會立刻接着運行後面的命令了。 這真是方便啊~再來,換個角度來想,萬一我想要在某個目錄底下創建一個文件,也就是說,如果該目錄存在的話, 那我才創建這個文件,如果不存在,那就算了。也就是說這兩個命令彼此之間是有相關性的, 前一個命令是否成功的運行與後一個命令是否要運行有關!那就得動用到 && 或 || 囉!

5.2.2 $? (命令回傳值) 與 && 或 ||

如同上面談到的,兩個命令之間有相依性,而這個相依性主要判斷的地方就在於前一個命令運行的結果是否正確。 還記得本章之前我們曾介紹過命令回傳值吧!嘿嘿!沒錯,您真聰明!就是透過這個回傳值啦! 再複習一次『若前一個命令運行的結果爲正確,在 Linux 底下會回傳一個 $? = 0 的值』。 那麼我們怎麼透過這個回傳值來判斷後續的命令是否要運行呢?這就得要藉由『 && 』及『 || 』的幫忙了! 注意喔,兩個 & 之間是沒有空格的!那個 | 則是 [Shift]+[] 的按鍵結果。
在這裏插入圖片描述
上述的 cmd1 及 cmd2 都是命令。好了,回到我們剛剛假想的情況,就是想要: (1)先判斷一個目錄是否存在; (2)若存在纔在該目錄底下創建一個文件。由於我們尚未介紹如何判斷式 (test) 的使用,在這裏我們使用 ls 以及回傳值來判斷目錄是否存在啦! 讓我們進行底下這個練習看看:

範例一:使用 ls 查閱目錄 /tmp/abc 是否存在,若存在則用 touch 創建 /tmp/abc/hehe 
[root@www ~]# ls /tmp/abc && touch /tmp/abc/hehe
ls: /tmp/abc: No such file or directory 
# ls 很乾脆的說明找不到該目錄,但並沒有 touch 的錯誤,表示 touch 並沒有運行

[root@www ~]# mkdir /tmp/abc
[root@www ~]# ls /tmp/abc && touch /tmp/abc/hehe
[root@www ~]# ll /tmp/abc
-rw-r--r-- 1 root root 0 Feb  7 12:43 hehe

看到了吧?如果 /tmp/abc 不存在時,touch 就不會被運行,若 /tmp/abc 存在的話,那麼 touch 就會開始運行囉! 很不錯用吧!不過,我們還得手動自行創建目錄,傷腦筋~能不能自動判斷,如果沒有該目錄就給予創建呢? 參考一下底下的例子先:

範例二:測試 /tmp/abc 是否存在,若不存在則予以創建,若存在就不作任何事情
[root@www ~]# rm -r /tmp/abc                <==先刪除此目錄以方便測試
[root@www ~]# ls /tmp/abc || mkdir /tmp/abc
ls: /tmp/abc: No such file or directory <==真的不存在喔!
[root@www ~]# ll /tmp/abc                  
total 0                                 <==結果出現了!有進行 mkdir

如果你一再重複『 ls /tmp/abc || mkdir /tmp/abc 』畫面也不會出現重複 mkdir 的錯誤!這是因爲 /tmp/abc 已經存在, 所以後續的 mkdir 就不會進行!這樣理解否?好了,讓我們再次的討論一下,如果我想要創建 /tmp/abc/hehe 這個文件, 但我並不知道 /tmp/abc 是否存在,那該如何是好?試看看:

範例三:我不清楚 /tmp/abc 是否存在,但就是要創建 /tmp/abc/hehe 文件
[root@www ~]# ls /tmp/abc || mkdir /tmp/abc && touch /tmp/abc/hehe

上面這個範例三總是會創建 /tmp/abc/hehe 的喔!不論 /tmp/abc 是否存在。那麼範例三應該如何解釋呢? 由於Linux 底下的命令都是由左往右運行的,所以範例三有幾種結果我們來分析一下:

  • (1)若 /tmp/abc 不存在故回傳 $?≠0,則 (2)因爲 || 遇到非爲 0 的 $? 故開始 mkdir /tmp/abc,由於 mkdir /tmp/abc 會成功進行,所以回傳 $?=0 (3)因爲 && 遇到 $?=0 故會運行 touch /tmp/abc/hehe,最終 hehe 就被創建了;

  • (1)若 /tmp/abc 存在故回傳 $?=0,則 (2)因爲 || 遇到 0 的 $? 不會進行,此時 $?=0 繼續向後傳,故 (3)因爲 && 遇到 $?=0 就開始創建 /tmp/abc/hehe 了!最終 /tmp/abc/hehe 被創建起來。

整個流程圖示如下:
在這裏插入圖片描述
上面這張圖顯示的兩股數據中,上方的線段爲不存在 /tmp/abc 時所進行的命令行爲,下方的線段則是存在 /tmp/abc 所在的命令行爲。如上所述,下方線段由於存在 /tmp/abc 所以導致 $?=0 ,讓中間的 mkdir 就不運行了! 並將 $?=0 繼續往後傳給後續的 touch 去利用啦!瞭乎?在任何時刻你都可以拿上面這張圖作爲示意! 讓我們來想想底下這個例題吧!

例題:
以 ls 測試 /tmp/vbirding 是否存在,若存在則顯示 “exist” ,若不存在,則顯示 “not exist”!
答:
這又牽涉到邏輯判斷的問題,如果存在就顯示某個數據,若不存在就顯示其他數據,那我可以這樣做:
ls /tmp/vbirding && echo “exist” || echo “not exist”
意思是說,當 ls /tmp/vbirding 運行後,若正確,就運行 echo “exist” ,若有問題,就運行 echo “not exist” !那如果寫成如下的狀況會出現什麼?
ls /tmp/vbirding || echo “not exist” && echo “exist”
這其實是有問題的,爲什麼呢?由圖 5.2.1 的流程介紹我們知道命令是一個一個往後運行, 因此在上面的例子當中,如果 /tmp/vbirding 不存在時,他會進行如下動作:
若 ls /tmp/vbirding 不存在,因此回傳一個非爲 0 的數值;
接下來經過 || 的判斷,發現前一個命令回傳非爲 0 的數值,因此,程序開始運行 echo “not exist” ,而 echo “not exist” 程序肯定可以運行成功,因此會回傳一個 0 值給後面的命令;

經過 && 的判斷,咦!是 0 啊!所以就開始運行 echo “exist” 。
所以啊,嘿嘿!第二個例子裏面竟然會同時出現 not exist 與 exist 呢!真神奇~

經過這個例題的練習,你應該會了解,由於命令是一個接着一個去運行的,因此,如果真要使用判斷, 那麼這個 && 與 || 的順序就不能搞錯。一般來說,假設判斷式有三個,也就是:

command1 && command2 || command3
而且順序通常不會變,因爲一般來說, command2 與 command3 會放置肯定可以運行成功的命令, 因此,依據上面例題的邏輯分析,您就會曉得爲何要如此放置囉~這很有用的啦!而且…考試也很常考~

6. 管道命令 (pipe)

就如同前面所說的, bash 命令運行的時候有輸出的數據會出現! 那麼如果這羣數據必需要經過幾道手續之後才能得到我們所想要的格式,應該如何來配置? 這就牽涉到管線命令的問題了 (pipe) ,管線命令使用的是『 | 』這個界定符號! 另外,管線命令與『連續下達命令』是不一樣的呦! 這點底下我們會再說明。底下我們先舉一個例子來說明一下簡單的管線命令。

假設我們想要知道 /etc/ 底下有多少文件,那麼可以利用 ls /etc 來查閱,不過, 因爲 /etc 底下的文件太多,導致一口氣就將屏幕塞滿了~不知道前面輸出的內容是啥?此時,我們可以透過 less 命令的協助,利用:

[root@www ~]# ls -al /etc | less

如此一來,使用 ls 命令輸出後的內容,就能夠被 less 讀取,並且利用 less 的功能,我們就能夠前後翻動相關的信息了!很方便是吧?我們就來了解一下這個管線命令『 | 』的用途吧! 其實這個管線命令『 | 』僅能處理經由前面一個命令傳來的正確信息,也就是 standard output 的信息,對於 stdandard error 並沒有直接處理的能力。那麼整體的管線命令可以使用下圖表示:
在這裏插入圖片描述
在每個管線後面接的第一個數據必定是『命令』喔!而且這個命令必須要能夠接受 standard input 的數據纔行,這樣的命令纔可以是爲『管線命令』,例如 less, more, head, tail 等都是可以接受 standard input 的管線命令啦。至於例如 ls, cp, mv 等就不是管線命令了!因爲 ls, cp, mv 並不會接受來自 stdin 的數據。 也就是說,管線命令主要有兩個比較需要注意的地方:

  • 管線命令僅會處理 standard output,對於 standard error output 會予以忽略
  • 管線命令必須要能夠接受來自前一個命令的數據成爲 standard input 繼續處理纔行。

多說無益,讓我們來玩一些管線命令吧!底下的咚咚對系統管理非常有幫助喔!

6.1 擷取命令: cut, grep

什麼是擷取命令啊?說穿了,就是將一段數據經過分析後,取出我們所想要的。或者是經由分析關鍵詞,取得我們所想要的那一行! 不過,要注意的是,一般來說,擷取信息通常是針對『一行一行』來分析的, 並不是整篇信息分析的喔~底下我們介紹兩個很常用的信息擷取命令:

6.1.1cut

cut 不就是『切』嗎?沒錯啦!這個命令可以將一段信息的某一段給他『切』出來~ 處理的信息是以『行』爲單位喔!底下我們就來談一談:

[root@www ~]# cut -d'分隔字符' -f fields <==用於有特定分隔字符
[root@www ~]# cut -c 字符區間            <==用於排列整齊的信息
選項與參數:
-d  :後面接分隔字符。與 -f 一起使用;
-f  :依據 -d 的分隔字符將一段信息分割成爲數段,用 -f 取出第幾段的意思;
-c  :以字符 (characters) 的單位取出固定字符區間;

範例一:將 PATH 變量取出,我要找出第五個路徑。
[root@www ~]# echo $PATH
/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/X11R6/bin:/usr/games:
# 1 |    2   |  3  |    4    |       5      |     6        |    7

[root@www ~]# echo $PATH | cut -d ':' -f 5
# 如同上面的數字顯示,我們是以『 : 』作爲分隔,因此會出現 /usr/local/bin 
# 那麼如果想要列出第 3 與第 5 呢?,就是這樣:
[root@www ~]# echo $PATH | cut -d ':' -f 3,5

範例二:將 export 輸出的信息,取得第 12 字符以後的所有字符串
[root@www ~]# export
declare -x HISTSIZE="1000"
declare -x INPUTRC="/etc/inputrc"
declare -x KDEDIR="/usr"
declare -x LANG="zh_TW.big5"
.....(其他省略).....
# 注意看,每個數據都是排列整齊的輸出!如果我們不想要『 declare -x 』時,
# 就得這麼做:

[root@www ~]# export | cut -c 12-
HISTSIZE="1000"
INPUTRC="/etc/inputrc"
KDEDIR="/usr"
LANG="zh_TW.big5"
.....(其他省略).....
# 知道怎麼回事了吧?用 -c 可以處理比較具有格式的輸出數據!
# 我們還可以指定某個範圍的值,例如第 12-20 的字符,就是 cut -c 12-20 等等!

範例三:用 last 將顯示的登陸者的信息中,僅留下用戶大名
[root@www ~]# last
root   pts/1    192.168.201.101  Sat Feb  7 12:35   still logged in
root   pts/1    192.168.201.101  Fri Feb  6 12:13 - 18:46  (06:33)
root   pts/1    192.168.201.254  Thu Feb  5 22:37 - 23:53  (01:16)
# last 可以輸出『賬號/終端機/來源/日期時間』的數據,並且是排列整齊的

[root@www ~]# last | cut -d ' ' -f 1
# 由輸出的結果我們可以發現第一個空白分隔的字段代表賬號,所以使用如上命令:
# 但是因爲 root   pts/1 之間空格有好幾個,並非僅有一個,所以,如果要找出 
# pts/1 其實不能以 cut -d ' ' -f 1,2 喔!輸出的結果會不是我們想要的。

cut 主要的用途在於將『同一行裏面的數據進行分解!』最常使用在分析一些數據或文字數據的時候! 這是因爲有時候我們會以某些字符當作分割的參數,然後來將數據加以切割,以取得我們所需要的數據。 鳥哥也很常使用這個功能呢!尤其是在分析 log 文件的時候!不過,cut 在處理多空格相連的數據時,可能會比較喫力一點。

6.1.2 grep

剛剛的 cut 是將一行信息當中,取出某部分我們想要的,而 grep 則是分析一行信息, 若當中有我們所需要的信息,就將該行拿出來~簡單的語法是這樣的:

[root@www ~]# grep [-acinv] [--color=auto] '搜尋字符串' filename
選項與參數:
-a :將 binary 文件以 text 文件的方式搜尋數據
-c :計算找到 '搜尋字符串' 的次數
-i :忽略大小寫的不同,所以大小寫視爲相同
-n :順便輸出行號
-v :反向選擇,亦即顯示出沒有 '搜尋字符串' 內容的那一行!
--color=auto :可以將找到的關鍵詞部分加上顏色的顯示喔!

範例一:將 last 當中,有出現 root 的那一行就取出來;
[root@www ~]# last | grep 'root'

範例二:與範例一相反,只要沒有 root 的就取出!
[root@www ~]# last | grep -v 'root'

範例三:在 last 的輸出信息中,只要有 root 就取出,並且僅取第一欄
[root@www ~]# last | grep 'root' |cut -d ' ' -f1
# 在取出 root 之後,利用上個命令 cut 的處理,就能夠僅取得第一欄囉!

範例四:取出 /etc/man.config 內含 MANPATH 的那幾行
[root@www ~]# grep --color=auto 'MANPATH' /etc/man.config
....(前面省略)....
MANPATH_MAP     /usr/X11R6/bin          /usr/X11R6/man
MANPATH_MAP     /usr/bin/X11            /usr/X11R6/man
MANPATH_MAP     /usr/bin/mh             /usr/share/man
# 神奇的是,如果加上 --color=auto 的選項,找到的關鍵詞部分會用特殊顏色顯示喔!

grep 是個很棒的命令喔!他支持的語法實在是太多了~用在正規表示法裏頭, 能夠處理的數據實在是多的很~不過,我們這裏先不談正規表示法~下一章再來說明~ 您先了解一下, grep 可以解析一行文字,取得關鍵詞,若該行有存在關鍵詞,就會整行列出來!

6.2 排序命令: sort, uniq, wc

很多時候,我們都會去計算一次數據裏頭的相同型態的數據總數,舉例來說, 使用 last 可以查得這個月份有登陸主機者的身份。那麼我可以針對每個使用者查出他們的總登陸次數嗎? 此時就得要排序與計算之類的命令來輔助了!底下我們介紹幾個好用的排序與統計命令喔!

6.2.1 sort

sort 是很有趣的命令,他可以幫我們進行排序,而且可以依據不同的數據型態來排序喔! 例如數字與文字的排序就不一樣。此外,排序的字符與語系的編碼有關,因此, 如果您需要排序時,建議使用 LANG=C 來讓語系統一,數據排序比較好一些。

[root@www ~]# sort [-fbMnrtuk] [file or stdin]
選項與參數:
-f  :忽略大小寫的差異,例如 A 與 a 視爲編碼相同;
-b  :忽略最前面的空格符部分;
-M  :以月份的名字來排序,例如 JAN, DEC 等等的排序方法;
-n  :使用『純數字』進行排序(默認是以文字型態來排序的);
-r  :反向排序;
-u  :就是 uniq ,相同的數據中,僅出現一行代表;
-t  :分隔符,默認是用 [tab] 鍵來分隔;
-k  :以那個區間 (field) 來進行排序的意思

範例一:個人賬號都記錄在 /etc/passwd 下,請將賬號進行排序。
[root@www ~]# cat /etc/passwd | sort
adm:x:3:4:adm:/var/adm:/sbin/nologin
apache:x:48:48:Apache:/var/www:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
# 鳥哥省略很多的輸出~由上面的數據看起來, sort 是默認『以第一個』數據來排序,
# 而且默認是以『文字』型態來排序的喔!所以由 a 開始排到最後囉!

範例二:/etc/passwd 內容是以 : 來分隔的,我想以第三欄來排序,該如何?
[root@www ~]# cat /etc/passwd | sort -t ':' -k 3
root:x:0:0:root:/root:/bin/bash
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
# 看到特殊字體的輸出部分了吧?怎麼會這樣排列啊?呵呵!沒錯啦~
# 如果是以文字型態來排序的話,原本就會是這樣,想要使用數字排序:
# cat /etc/passwd | sort -t ':' -k 3 -n
# 這樣纔行啊!用那個 -n 來告知 sort 以數字來排序啊!

範例三:利用 last ,將輸出的數據僅取賬號,並加以排序
[root@www ~]# last | cut -d ' ' -f1 | sort

sort 同樣是很常用的命令呢!因爲我們常常需要比較一些信息啦! 舉個上面的第二個例子來說好了!今天假設你有很多的賬號,而且你想要知道最大的使用者 ID 目前到哪一號了!呵呵!使用 sort 一下子就可以知道答案咯!當然其使用還不止此啦! 有空的話不妨玩一玩!

6.2.2 uniq

如果我排序完成了,想要將重複的數據僅列出一個顯示,可以怎麼做呢?

[root@www ~]# uniq [-ic]
選項與參數:
-i  :忽略大小寫字符的不同;
-c  :進行計數

範例一:使用 last 將賬號列出,僅取出賬號欄,進行排序後僅取出一位;
[root@www ~]# last | cut -d ' ' -f1 | sort | uniq

範例二:承上題,如果我還想要知道每個人的登陸總次數呢?
[root@www ~]# last | cut -d ' ' -f1 | sort | uniq -c
      1
     12 reboot
     41 root
      1 wtmp
# 從上面的結果可以發現 reboot 有 12 次, root 登陸則有 41 次!
# wtmp 與第一行的空白都是 last 的默認字符,那兩個可以忽略的!

這個命令用來將『重複的行刪除掉只顯示一個』,舉個例子來說, 你要知道這個月份登陸你主機的用戶有誰,而不在乎他的登陸次數,那麼就使用上面的範例, (1)先將所有的數據列出;(2)再將人名獨立出來;(3)經過排序;(4)只顯示一個! 由於這個命令是在將重複的東西減少,所以當然需要『配合排序過的文件』來處理囉!

6.2.3 wc

如果我想要知道 /etc/man.config 這個文件裏面有多少字?多少行?多少字符的話, 可以怎麼做呢?其實可以利用 wc 這個命令來達成喔!他可以幫我們計算輸出的信息的整體數據!

[root@www ~]# wc [-lwm]
選項與參數:
-l  :僅列出行;
-w  :僅列出多少字(英文單字);
-m  :多少字符;

範例一:那個 /etc/man.config 裏面到底有多少相關字、行、字符數?
[root@www ~]# cat /etc/man.config | wc 
    141     722    4617
# 輸出的三個數字中,分別代表: 『行、字數、字符數』

範例二:我知道使用 last 可以輸出登陸者,但是 last 最後兩行並非賬號內容,
        那麼請問,我該如何以一行命令串取得這個月份登陸系統的總人次?
[root@www ~]# last | grep [a-zA-Z] | grep -v 'wtmp' | wc -l 
# 由於 last 會輸出空白行與 wtmp 字樣在最底下兩行,因此,我利用
# grep 取出非空白行,以及去除 wtmp 那一行,在計算行數,就能夠了解囉!

wc 也可以當作命令?這可不是上洗手間的 WC 呢!這是相當有用的計算文件內容的一個工具組喔!舉個例子來說, 當你要知道目前你的賬號文件中有多少個賬號時,就使用這個方法:『 cat /etc/passwd | wc -l 』啦!因爲 /etc/passwd 裏頭一行代表一個使用者呀! 所以知道行數就曉得有多少的賬號在裏頭了!而如果要計算一個文件裏頭有多少個字符時,就使用 wc -m 這個選項吧!

6.3 雙向重導向: tee

想個簡單的東西,我們由前一節知道 > 會將數據流整個傳送給文件或裝置,因此我們除非去讀取該文件或裝置, 否則就無法繼續利用這個數據流。萬一我想要將這個數據流的處理過程中將某段信息存下來,應該怎麼做? 利用 tee 就可以囉~我們可以這樣簡單的看一下:
在這裏插入圖片描述
tee 會同時將數據流分送到文件去與屏幕 (screen);而輸出到屏幕的,其實就是 stdout ,可以讓下個命令繼續處理喔!

[root@www ~]# tee [-a] file
選項與參數:
-a  :以累加 (append) 的方式,將數據加入 file 當中!

[root@www ~]# last | tee last.list | cut -d " " -f1
# 這個範例可以讓我們將 last 的輸出存一份到 last.list 文件中;

[root@www ~]# ls -l /home | tee ~/homefile | more
# 這個範例則是將 ls 的數據存一份到 ~/homefile ,同時屏幕也有輸出信息!

[root@www ~]# ls -l / | tee -a ~/homefile | more
# 要注意! tee 後接的文件會被覆蓋,若加上 -a 這個選項則能將信息累加。

tee 可以讓 standard output 轉存一份到文件內並將同樣的數據繼續送到屏幕去處理! 這樣除了可以讓我們同時分析一份數據並記錄下來之外,還可以作爲處理一份數據的中間緩存盤記錄之用! tee 這傢伙在很多選擇/填充的認證考試中很容易考呢!

6.4 字符轉換命令: tr, col, join, paste, expand

我們在 vim 程序編輯器當中,提到過 DOS 斷行字符與 Unix 斷行字符的不同,並且可以使用 dos2unix 與 unix2dos 來完成轉換。好了,那麼思考一下,是否還有其他常用的字符替代? 舉例來說,要將大寫改成小寫,或者是將數據中的 [tab] 按鍵轉成空格鍵?還有,如何將兩篇信息整合成一篇? 底下我們就來介紹一下這些字符轉換命令在管線當中的使用方法:

6.4.1 tr

tr 可以用來刪除一段信息當中的文字,或者是進行文字信息的替換!

[root@www ~]# tr [-ds] SET1 ...
選項與參數:
-d  :刪除信息當中的 SET1 這個字符串;
-s  :取代掉重複的字符!

範例一:將 last 輸出的信息中,所有的小寫變成大寫字符:
[root@www ~]# last | tr '[a-z]' '[A-Z]'
# 事實上,沒有加上單引號也是可以運行的,如:『 last | tr [a-z] [A-Z] 』

範例二:將 /etc/passwd 輸出的信息中,將冒號 (:) 刪除
[root@www ~]# cat /etc/passwd | tr -d ':'

範例三:將 /etc/passwd 轉存成 dos 斷行到 /root/passwd 中,再將 ^M 符號刪除
[root@www ~]# cp /etc/passwd /root/passwd && unix2dos /root/passwd
[root@www ~]# file /etc/passwd /root/passwd
/etc/passwd:  ASCII text
/root/passwd: ASCII text, with CRLF line terminators <==就是 DOS 斷行
[root@www ~]# cat /root/passwd | tr -d '\r' > /root/passwd.linux
# 那個 \r 指的是 DOS 的斷行字符,關於更多的字符,請參考 man tr
[root@www ~]# ll /etc/passwd /root/passwd*
-rw-r--r-- 1 root root 1986 Feb  6 17:55 /etc/passwd
-rw-r--r-- 1 root root 2030 Feb  7 15:55 /root/passwd
-rw-r--r-- 1 root root 1986 Feb  7 15:57 /root/passwd.linux
# 處理過後,發現文件大小與原本的 /etc/passwd 就一致了!

其實這個命令也可以寫在『正規表示法』裏頭!因爲他也是由正規表示法的方式來取代數據的! 以上面的例子來說,使用 [] 可以配置一串字呢!也常常用來取代文件中的怪異符號! 例如上面第三個例子當中,可以去除 DOS 文件留下來的 ^M 這個斷行的符號!這東西相當的有用!相信處理 Linux & Windows 系統中的人們最麻煩的一件事就是這個事情啦!亦即是 DOS 底下會自動的在每行行尾加入 ^M 這個斷行符號!這個時候我們可以使用這個 tr 來將 ^M 去除! ^M 可以使用 \r 來代替之!

6.4.2 col

[root@www ~]# col [-xb]
選項與參數:
-x  :將 tab 鍵轉換成對等的空格鍵
-b  :在文字內有反斜槓 (/) 時,僅保留反斜槓最後接的那個字符

範例一:利用 cat -A 顯示出所有特殊按鍵,最後以 col 將 [tab] 轉成空白
[root@www ~]# cat -A /etc/man.config  <==此時會看到很多 ^I 的符號,那就是 tab
[root@www ~]# cat /etc/man.config | col -x | cat -A | more
# 嘿嘿!如此一來, [tab] 按鍵會被取代成爲空格鍵,輸出就美觀多了!

範例二:將 col 的 man page 轉存成爲 /root/col.man 的純文本檔
[root@www ~]# man col > /root/col.man
[root@www ~]# vi /root/col.man
COL(1)          BSD General Commands Manual               COL(1)

N^HNA^HAM^HME^HE
     c^Hco^Hol^Hl - filter reverse line feeds from input

S^HSY^HYN^HNO^HOP^HPS^HSI^HIS^HS
     c^Hco^Hol^Hl [-^H-b^Hbf^Hfp^Hpx^Hx] [-^H-l^Hl _^Hn_^Hu_^Hm]
# 你沒看錯!由於 man page 內有些特殊按鈕會用來作爲類似特殊按鍵與顏色顯示,
# 所以這個文件內就會出現如上所示的一堆怪異字符(有 ^ 的)

[root@www ~]# man col | col -b > /root/col.man

雖然 col 有他特殊的用途,不過,很多時候,他可以用來簡單的處理將 [tab] 按鍵取代成爲空格鍵! 例如上面的例子當中,如果使用 cat -A 則 [tab] 會以 ^I 來表示。 但經過 col -x 的處理,則會將 [tab] 取代成爲對等的空格鍵!此外, col 經常被利用於將 man page 轉存爲純文本文件以方便查閱的功能!如上述的範例二!

6.4.3 join

join 看字面上的意義 (加入/參加) 就可以知道,他是在處理兩個文件之間的數據, 而且,主要是在處理『兩個文件當中,有 “相同數據” 的那一行,纔將他加在一起』的意思。我們利用底下的簡單例子來說明:

[root@www ~]# join [-ti12] file1 file2
選項與參數:
-t  :join 默認以空格符分隔數據,並且比對『第一個字段』的數據,
      如果兩個文件相同,則將兩筆數據聯成一行,且第一個字段放在第一個!
-i  :忽略大小寫的差異;
-1  :這個是數字的 1 ,代表『第一個文件要用那個字段來分析』的意思;
-2  :代表『第二個文件要用那個字段來分析』的意思。

範例一:用 root 的身份,將 /etc/passwd 與 /etc/shadow 相關數據整合成一欄
[root@www ~]# head -n 3 /etc/passwd /etc/shadow
==> /etc/passwd <==
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

==> /etc/shadow <==
root:$1$/3AQpE5e$y9A/D0bh6rElAs:14120:0:99999:7:::
bin:*:14126:0:99999:7:::
daemon:*:14126:0:99999:7:::
# 由輸出的數據可以發現這兩個文件的最左邊字段都是賬號!且以 : 分隔

[root@www ~]# join -t ':' /etc/passwd /etc/shadow
root:x:0:0:root:/root:/bin/bash:$1$/3AQpE5e$y9A/D0bh6rElAs:14120:0:99999:7:::
bin:x:1:1:bin:/bin:/sbin/nologin:*:14126:0:99999:7:::
daemon:x:2:2:daemon:/sbin:/sbin/nologin:*:14126:0:99999:7:::
# 透過上面這個動作,我們可以將兩個文件第一字段相同者整合成一行!
# 第二個文件的相同字段並不會顯示(因爲已經在第一行了嘛!)

範例二:我們知道 /etc/passwd 第四個字段是 GID ,那個 GID 記錄在 
        /etc/group 當中的第三個字段,請問如何將兩個文件整合?
[root@www ~]# head -n 3 /etc/passwd /etc/group
==> /etc/passwd <==
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

==> /etc/group <==
root:x:0:root
bin:x:1:root,bin,daemon
daemon:x:2:root,bin,daemon
# 從上面可以看到,確實有相同的部分喔!趕緊來整合一下!

[root@www ~]# join -t ':' -1 4 /etc/passwd -2 3 /etc/group
0:root:x:0:root:/root:/bin/bash:root:x:root
1:bin:x:1:bin:/bin:/sbin/nologin:bin:x:root,bin,daemon
2:daemon:x:2:daemon:/sbin:/sbin/nologin:daemon:x:root,bin,daemon
# 同樣的,相同的字段部分被移動到最前面了!所以第二個文件的內容就沒再顯示。
# 請讀者們配合上述顯示兩個文件的實際內容來比對!

這個 join 在處理兩個相關的數據文件時,就真的是很有幫助的啦! 例如上面的案例當中,我的 /etc/passwd, /etc/shadow, /etc/group 都是有相關性的, 其中 /etc/passwd, /etc/shadow 以賬號爲相關性,至於 /etc/passwd, /etc/group 則以所謂的 GID (賬號的數字定義) 來作爲他的相關性。根據這個相關性, 我們可以將有關係的數據放置在一起!這在處理數據可是相當有幫助的! 但是上面的例子有點難,希望您可以靜下心好好的看一看原因喔!

此外,需要特別注意的是,在使用 join 之前,你所需要處理的文件應該要事先經過排序 (sort) 處理! 否則有些比對的項目會被略過呢!特別注意了!

6.4.4 paste

這個 paste 就要比 join 簡單多了!相對於 join 必須要比對兩個文件的數據相關性, paste 就直接『將兩行貼在一起,且中間以 [tab] 鍵隔開』而已!簡單的使用方法:

[root@www ~]# paste [-d] file1 file2
選項與參數:
-d  :後面可以接分隔字符。默認是以 [tab] 來分隔的!
-   :如果 file 部分寫成 - ,表示來自 standard input 的數據的意思。

範例一:將 /etc/passwd 與 /etc/shadow 同一行貼在一起
[root@www ~]# paste /etc/passwd /etc/shadow
bin:x:1:1:bin:/bin:/sbin/nologin        bin:*:14126:0:99999:7:::
daemon:x:2:2:daemon:/sbin:/sbin/nologin daemon:*:14126:0:99999:7:::
adm:x:3:4:adm:/var/adm:/sbin/nologin    adm:*:14126:0:99999:7:::
# 注意喔!同一行中間是以 [tab] 按鍵隔開的!

範例二:先將 /etc/group 讀出(用 cat),然後與範例一貼上一起!且僅取出前三行
[root@www ~]# cat /etc/group|paste /etc/passwd /etc/shadow -|head -n 3
# 這個例子的重點在那個 - 的使用!那玩意兒常常代表 stdin 喔!

6.4.5 expand

這玩意兒就是在將 [tab] 按鍵轉成空格鍵啦~可以這樣玩:

[root@www ~]# expand [-t] file
選項與參數:
-t  :後面可以接數字。一般來說,一個 tab 按鍵可以用 8 個空格鍵取代。
      我們也可以自行定義一個 [tab] 按鍵代表多少個字符呢!

範例一:將 /etc/man.config 內行首爲 MANPATH 的字樣就取出;僅取前三行;
[root@www ~]# grep '^MANPATH' /etc/man.config | head -n 3
MANPATH /usr/man
MANPATH /usr/share/man
MANPATH /usr/local/man
# 行首的代表標誌爲 ^ ,這個我們留待下節介紹!先有概念即可!

範例二:承上,如果我想要將所有的符號都列出來?(用 cat)
[root@www ~]# grep '^MANPATH' /etc/man.config | head -n 3 |cat -A
MANPATH^I/usr/man$
MANPATH^I/usr/share/man$
MANPATH^I/usr/local/man$
# 發現差別了嗎?沒錯~ [tab] 按鍵可以被 cat -A 顯示成爲 ^I 

範例三:承上,我將 [tab] 按鍵配置成 6 個字符的話?
[root@www ~]# grep '^MANPATH' /etc/man.config | head -n 3 | \
>  expand -t 6 - | cat -A
MANPATH     /usr/man$
MANPATH     /usr/share/man$
MANPATH     /usr/local/man$
123456123456123456.....
# 仔細看一下上面的數字說明,因爲我是以 6 個字符來代表一個 [tab] 的長度,所以,
# MAN... 到 /usr 之間會隔 12 (兩個 [tab]) 個字符喔!如果 tab 改成 9 的話,
# 情況就又不同了!這裏也不好理解~您可以多配置幾個數字來查閱就曉得!

expand 也是挺好玩的~他會自動將 [tab] 轉成空格鍵~所以,以上面的例子來說, 使用 cat -A 就會查不到 ^I 的字符囉~此外,因爲 [tab] 最大的功能就是格式排列整齊! 我們轉成空格鍵後,這個空格鍵也會依據我們自己的定義來添加大小~ 所以,並不是一個 ^I 就會換成 8 個空白喔!這個地方要特別注意的哩! 此外,您也可以參考一下 unexpand 這個將空白轉成 [tab] 的命令功能啊! _

6.5 分割命令: split

如果你有文件太大,導致一些攜帶式裝置無法複製的問題,嘿嘿!找 split 就對了! 他可以幫你將一個大文件,依據文件大小或行數來分割,就可以將大文件分割成爲小文件了! 快速又有效啊!真不錯~

[root@www ~]# split [-bl] file PREFIX
選項與參數:
-b  :後面可接欲分割成的文件大小,可加單位,例如 b, k, m 等;
-l  :以行數來進行分割。
PREFIX :代表前導符的意思,可作爲分割文件的前導文字。

範例一:我的 /etc/termcap 有七百多K,若想要分成 300K 一個文件時?
[root@www ~]# cd /tmp; split -b 300k /etc/termcap termcap
[root@www tmp]# ll -k termcap*
-rw-r--r-- 1 root root 300 Feb  7 16:39 termcapaa
-rw-r--r-- 1 root root 300 Feb  7 16:39 termcapab
-rw-r--r-- 1 root root 189 Feb  7 16:39 termcapac
# 那個檔名可以隨意取的啦!我們只要寫上前導文字,小文件就會以
# xxxaa, xxxab, xxxac 等方式來創建小文件的!

範例二:如何將上面的三個小文件合成一個文件,檔名爲 termcapback
[root@www tmp]# cat termcap* >> termcapback
# 很簡單吧?就用數據流重導向就好啦!簡單!

範例三:使用 ls -al / 輸出的信息中,每十行記錄成一個文件
[root@www tmp]# ls -al / | split -l 10 - lsroot
[root@www tmp]# wc -l lsroot*
  10 lsrootaa
  10 lsrootab
   6 lsrootac
  26 total
# 重點在那個 - 啦!一般來說,如果需要 stdout/stdin 時,但偏偏又沒有文件,
# 有的只是 - 時,那麼那個 - 就會被當成 stdin 或 stdout ~

在 Windows 操作系統下,你要將文件分割需要如何作?傷腦筋吧!在 Linux 底下就簡單的多了!你要將文件分割的話,那麼就使用 -b size 來將一個分割的文件限制其大小,如果是行數的話,那麼就使用 -l line 來分割!好用的很!如此一來,你就可以輕易的將你的文件分割成軟盤 (floppy) 的大小,方便你 copy 囉!

6.6 參數代換: xargs

xargs 是在做什麼的呢?就以字面上的意義來看, x 是加減乘除的乘號,args 則是 arguments (參數) 的意思,所以說,這個玩意兒就是在產生某個命令的參數的意思! xargs 可以讀入 stdin 的數據,並且以空格符或斷行字符作爲分辨,將 stdin 的數據分隔成爲 arguments 。 因爲是以空格符作爲分隔,所以,如果有一些檔名或者是其他意義的名詞內含有空格符的時候, xargs 可能就會誤判了~他的用法其實也還滿簡單的!就來看一看先!

[root@www ~]# xargs [-0epn] command
選項與參數:
-0  :如果輸入的 stdin 含有特殊字符,例如 `, \, 空格鍵等等字符時,這個 -0 參數
      可以將他還原成一般字符。這個參數可以用於特殊狀態喔!
-e  :這個是 EOF (end of file) 的意思。後面可以接一個字符串,當 xargs 分析到
      這個字符串時,就會停止繼續工作!
-p  :在運行每個命令的 argument 時,都會詢問使用者的意思;
-n  :後面接次數,每次 command 命令運行時,要使用幾個參數的意思。看範例三。
當 xargs 後面沒有接任何的命令時,默認是以 echo 來進行輸出喔!

範例一:將 /etc/passwd 內的第一欄取出,僅取三行,使用 finger 這個命令將每個
        賬號內容秀出來
[root@www ~]# cut -d':' -f1 /etc/passwd |head -n 3| xargs finger
Login: root                             Name: root
Directory: /root                        Shell: /bin/bash
Never logged in.
No mail.
No Plan.
......底下省略.....
# 由 finger account 可以取得該賬號的相關說明內容,例如上面的輸出就是 finger root
# 後的結果。在這個例子當中,我們利用 cut 取出賬號名稱,用 head 取出三個賬號,
# 最後則是由 xargs 將三個賬號的名稱變成 finger 後面需要的參數!

範例二:同上,但是每次運行 finger 時,都要詢問使用者是否動作?
[root@www ~]# cut -d':' -f1 /etc/passwd |head -n 3| xargs -p finger
finger root bin daemon ?...y
.....(底下省略)....
# 呵呵!這個 -p 的選項可以讓用戶的使用過程中,被詢問到每個命令是否運行!

範例三:將所有的 /etc/passwd 內的賬號都以 finger 查閱,但一次僅查閱五個賬號
[root@www ~]# cut -d':' -f1 /etc/passwd | xargs -p -n 5 finger
finger root bin daemon adm lp ?...y
.....(中間省略)....
finger uucp operator games gopher ftp ?...y
.....(底下省略)....
# 在這裏鳥哥使用了 -p 這個參數來讓您對於 -n 更有概念。一般來說,某些命令後面
# 可以接的 arguments 是有限制的,不能無限制的累加,此時,我們可以利用 -n
# 來幫助我們將參數分成數個部分,每個部分分別再以命令來運行!這樣就 OK 啦!^_^

範例四:同上,但是當分析到 lp 就結束這串命令?
[root@www ~]# cut -d':' -f1 /etc/passwd | xargs -p -e'lp' finger
finger root bin daemon adm ?...
# 仔細與上面的案例做比較。也同時注意,那個 -e'lp' 是連在一起的,中間沒有空格鍵。
# 上個例子當中,第五個參數是 lp 啊,那麼我們下達 -e'lp' 後,則分析到 lp
# 這個字符串時,後面的其他 stdin 的內容就會被 xargs 捨棄掉了!

其實,在 man xargs 裏面就有三四個小范例,您可以自行參考一下內容。 此外, xargs 真的是很好用的一個玩意兒!您真的需要好好的參詳參詳!會使用 xargs 的原因是, 很多命令其實並不支持管線命令,因此我們可以透過 xargs 來提供該命令引用 standard input 之用!舉例來說,我們使用如下的範例來說明:

範例五:找出 /sbin 底下具有特殊權限的檔名,並使用 ls -l 列出詳細屬性
[root@www ~]# find /sbin -perm +7000 | ls -l
# 結果竟然僅有列出 root 所在目錄下的文件!這不是我們要的!
# 因爲 ll (ls) 並不是管線命令的原因啊!

[root@www ~]# find /sbin -perm +7000 | xargs ls -l
-rwsr-xr-x 1 root root 70420 May 25  2008 /sbin/mount.nfs
-rwsr-xr-x 1 root root 70424 May 25  2008 /sbin/mount.nfs4
-rwxr-sr-x 1 root root  5920 Jun 15  2008 /sbin/netreport
....(底下省略)....

6.7 關於減號 - 的用途

管線命令在 bash 的連續的處理程序中是相當重要的!另外,在 log file 的分析當中也是相當重要的一環, 所以請特別留意!另外,在管線命令當中,常常會使用到前一個命令的 stdout 作爲這次的 stdin , 某些命令需要用到文件名 (例如 tar) 來進行處理時,該 stdin 與 stdout 可以利用減號 “-” 來替代, 舉例來說:

[root@www ~]# tar -cvf - /home | tar -xvf -

上面這個例子是說:『我將 /home 裏面的文件給他打包,但打包的數據不是紀錄到文件,而是傳送到 stdout; 經過管線後,將 tar -cvf - /home 傳送給後面的 tar -xvf - 』。後面的這個 - 則是取用前一個命令的 stdout, 因此,我們就不需要使用 file 了!這是很常見的例子喔!注意注意!

7. 重點回顧

  • 由於核心在內存中是受保護的區塊,因此我們必須要透過『 Shell 』將我們輸入的命令與 Kernel 溝通,好讓 Kernel 可以控制硬件來正確無誤的工作
  • 學習 shell 的原因主要有:文字接口的 shell 在各大 distribution 都一樣;遠程管理時文字接口速度較快; shell 是管理 Linux 系統非常重要的一環,因爲 Linux 內很多控制都是以 shell 撰寫的。
  • 系統合法的 shell 均寫在 /etc/shells 文件中;
  • 用戶默認登陸取得的 shell 記錄於 /etc/passwd 的最後一個字段;
  • bash 的功能主要有:命令編修能力;命令與文件補全功能;命令別名配置功能;工作控制、前景背景控制;程序化腳本;通配符
  • type 可以用來找到運行命令爲何種類型,亦可用於與 which 相同的功能;
  • 變量就是以一組文字或符號等,來取代一些配置或者是一串保留的數據
  • 變量主要有環境變量與自定義變量,或稱爲全局變量與局部變量
  • 使用 env 與 export 可觀察環境變量,其中 export 可以將自定義變量轉成環境變量;
  • set 可以觀察目前 bash 環境下的所有變量;
  • $? 亦爲變量,是前一個命令運行完畢後的回傳值。在 Linux 回傳值爲 0 代表運行成功;
  • locale 可用於觀察語系數據;
  • 可用 read 讓用戶由鍵盤輸入變量的值
  • ulimit 可用以限制用戶使用系統的資源情況
  • bash 的配置文件主要分爲 login shell 與 non-login shell。login shell 主要讀取 /etc/profile 與~/.bash_profile, non-login shell 則僅讀取 ~/.bashrc
  • 通配符主要有: *, ?, [] 等等
  • 數據流重導向透過 >, 2>, < 之類的符號將輸出的信息轉到其他文件或裝置去;
  • 連續命令的下達可透過 ; && || 等符號來處理
  • 管線命令的重點是:『管線命令僅會處理 standard output,對於 standard error output 會予以忽略』 『管線命令必須要能夠接受來自前一個命令的數據成爲 standard input 繼續處理纔行。』
  • 本章介紹的管線命令主要有:cut, grep, sort, wc, uniq, tee, tr, col, join, paste, expand, split, xargs 等。

轉載自:第十一章、認識與學習 BASH

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章