Linux Basics-Linux Bash歷史和其概念名詞解釋part1
閱讀本文你將知道:查看更多BashFAQ.pdf
Bash的歷史及其特性
Bash的如何工作
Bash的概念解釋
前提知識:
對linux有一定基礎而且了解Bash的簡單命令
說明:
1. 資料參考將于文章后面列出,資料的參考源于wiki或者Bash作者寫的Bash簡介或者開源組織公開的手冊,原文的閱讀建議英文比較好的同學去參考
2. 對于Linux Bash的上的疑問和錯誤問題解答,可以參考我給的BashFAQ,亦可留言討論
3. 資料為整合
Part 1 Bash歷史及其簡介
Ken Thompson 在創建了UNIX之后,于 1971 年開發了第一個用于 UNIX 的 shell(命令行解釋器),名為 V6 shell。類似于它在 Multics 中的前身,這個 shell (/bin/sh) 是一個在內核外部執行的獨立用戶程序, Bourne shell 由 Stephen Bourne 在 AT&T Bell Labs 為 V7 UNIX 創建,作者在研究 ALGOL68 編譯器之后開發了 Bourne shell,所以您會發現它的語法比其他 shell 更加類似于 Algorithmic Language (ALGOL)。盡管使用 C 開發,源代碼本身甚至使用了宏賦予它一種 ALGOL68 特色。
Bourne shell 有兩個主要目標:用作一個命令解釋器來交互式執行操作系統命令,以及用于編寫腳本(編寫可通過 shell 調用的可重用腳本)。 除了取代 Thompson shell,Bourne shell 還提供了相對于其前身的多項優勢。Bourne 向腳本中引入了控制流、循環和變量,提供了一種更加強大的語言來與操作系統交互(包括交互式和非交互式)。該 shell 還允許您使用 shell 腳本作為過濾器,提供對處理信號的集成支持,但缺乏定義函數的能力。 最后,它整合了我們如今使用的許多功能,包括命令替換(使用反引號)和用于將保留的字符串文字嵌入到腳本中的 HERE 文檔。再后來就出現了Bash,Bash是出現在GUN操作系統上,常常用在Linux中,
Bash是 Bourne-Again Shell的簡寫 — 這是關于Bourne shell(sh)的一個雙關語(Bourne again / born again), 誕生于 1987 年,是作為 GNU 項目開發的,主要由Stephen Bourne所寫,它最最早期的作者是Brian Fox, Brian Fox是Free Software Foundation的雇員,之后Bash就由美國(Case Western Reserve University in Cleveland, Ohio) 凱斯西儲大學的志愿者接管維護,最后的發展就是現在集成到linux系統中成為系統的標配. 現在是由Chet Ramey管理,Bash是Linux系統預安裝的默認 shell 環境。
Bash 的優點之一是它具有內置的安全特性。Bash 會準確地記錄用戶輸入的命令,并把記錄寫到用戶主目錄中的隱藏文件 .bash_history 中。bash 的語法具有其他 shell 所沒有的許多擴展。與其他 shell 相比,bash 中的整數計算更高效,而且 bash 可以更方便地重定向標準輸出(stdout)和標準錯誤(stderr)。
Bash 還非常適合于安全性要求高的環境,它具有受限制的啟動模式,可以把 shell 中的用戶限制為只能執行一組確定的命令??梢酝ㄟ^編輯自己的 bash shell 登錄控制文件(即 .bashrc、.bash_profile、.bash_logout 和 .profile 等隱藏文件)定制登錄 shell。
基本 shell 架構
一種假想的 shell 的基本架構很簡單(Bourne 的 shell 就是一個證據)。在圖 2中可以看到,基本架構看起來類似一個管道,其中會分析和解析輸入,展開符號(使用各種方法,比如括號、波浪號、變量、參數擴展和替換,以及文件名生成),最終執行命令(使用 shell 內置的命令或外部命令)。
圖 2. 假想 shell 的簡單架構
bash 登錄過程
在登錄時,用戶通常執行一個全局概要文件和個人文件(.bash_profile 和 .bashrc)
圖 3. bash shell 登錄過程細節
在此之后,用戶就可以使用 bash shell 環境變量 $PATH 中指定的標準命令集,比如內置命令等
forking
命令或 bash shell 本身可能啟動(或生成)新的 shell 子進程以執行某一任務,這稱為 forking。當這個新進程(子進程)正在執行時,父進程仍然在運行。如果父進程先于子進程死亡,那么子進程就成了死進程(也稱為僵尸進程 )
Bash如何運行
參考如下:
Bash Component Architecture
bash的組件架構:
注釋:
1 input
輸入處理:
1.1 taking characters from the terminal or a file, breaking them into lines, and passing the lines to the shell parser to transform into commands.
將用戶輸入的內容輸送到命令流中,通過shell解釋器翻譯成命令
1.2 用戶輸入的內容Readline 庫來記錄用戶輸入的歷史命令,每個字符作為一個鍵盤映射或者調度表(keymap, or dispatch table)也可以進行multiple-character(多字符)處理,別人判別鍵盤輸入的快捷鍵,(ctrl+c就是終止程序的意思),每個字符八個字節
2 lexical analysis and Parsing
詞法分析解析
先是把字符流組成一個單詞(to separate the stream of characters into words),單詞是解析器操作的基本單位,它是由元字符(metacharacter)分隔的字符序列,元字符包括空格,制表符,&,分號等等
Shell解釋器在任何情況下接受從readline輸入的內容(readline就好比是read和line組成的一個單詞,作者是Brian Fox,同時也是bashl最初的作者,readline提供命令行編輯功能和命令歷史概念。read就是讀的意思,從鍵盤中讀取的意思,line的意思就是說,從鍵盤中read的字符character,把字符組裝成為line,這個時候line就可以理解為一條字符流,之后把line送到shell解釋器中,解釋器翻譯成為commend命令)
Shell使用轉義符來消除字符的特色意義,轉義符是用反斜杠表示,在解析之后,先進行字展開例如 $OSTYPE被替換為字符串 "linux-gnu",上述的例子是一個變量引用,變量的引用用$來引用,Bash的弱類型語言,除了幾個少數例外,其他一般都視作字符串,在處理順序上一般先是處理括號,比如pre{one,two,three}post會變成preonepost pretwopost prethreepost
Bash支持重定向,管道,內建命令,復合命令,作業控制等等
Part 2 附錄:(概念解釋)
1.Syntactic Units and Primitives
語法和原始數據類型
原始數據類型:
1對于Bash,有三種基本的標記(稱為token),它們分別是: reserved words(保留字), words, and operators(運算符), reserved words一般是對于shell有特色意義的保留詞,比如控制流程語句中的if和while, operators包括比如 | 或者 > 等
2.Bash中的元字符
Bash中的元字符是一些有著特殊含意的字符, 在Bash的解釋中不是按照字面含意來解釋的:
Charactors |
Meaning |
* ? [ ] ~ + – @ ! |
Filename metacharactors |
Filename Metacharactors
In computer programming, the verb glob or globbing is used to refer to an instance of pattern matching behavior. The noun "glob" is sometimes used to refer to a particular pattern, e.g. "use the glob .log to match all those log files".
Bash用來匹配文件名的元字符:
匹配零個或者多個字符
? 匹配單個字符
[abc…] 匹配包含在集合中任意字符
[!abc…] 匹配不包含在集合中的任意字符
[a-zA-Z0-9] 匹配指定的字符范圍中的字符
(可移植性不強,根據區域設置,字符范圍可能包含其他字符)
~ 當前用戶的Home目錄
~name name用戶的Home目錄
~+ 當前的工作目錄($PWD)
~- 上次的工作目錄($OLDPWD)
下面即為一些特殊字符及其用途:
-
[空白]:空白字符(whitespace,包括空格、制表符及新行)。BASH 使用空白字符來判定詞語的開頭與結尾。每個命令的第一個詞語為命令名稱;任何其他詞語則為該命令的參數。
-
$:展開字符。該字符用于大多數的替換,包括參數展開(變量代換)。以后會詳述。
-
'文本':單引號可用于防止其中的文本被 shell 以任何形式展開,并阻止其分為多個詞語或參數。他們也可防止任何在其內部的特殊字符的特殊意義。
-
"文本":雙引號阻止其內部的文本分割為多個詞語或參數,但它允許進行代換。它可阻止其內部的多數特殊字符——簡單地說,所有除 $ 以外的字符。
-
#:注釋字符。任何以 # 為開頭的詞語為注釋的開始,并一直到行末。shell 不處理注釋。
-
;:命令分隔符。用戶在把多個命令放在一行寫時,分號用來把它們隔開。
-
\:轉義字符:反斜杠用來阻止下一個字符的特殊用途。它在雙引號內或引號外有效,但在單引號內無效。
-
~:波浪線代表家目錄。它單獨使用或在后接一個 /時,與 $HOME 相同。在后接一個用戶名時,則表示該用戶的家目錄。例:cd ~john/bin; cp coolscript ~/bin
-
> 或 <:重定向字符。這些字符用來更改(重定向)一命令的輸入和/或輸出。重定向會在以后談到。
-
|:管道可以將一命令的輸出傳遞給另一命令作為輸入。
-
表達式:測試表達式。測試表達式將條件表達式作為邏輯語句來求值,以判定其為“真”還是“假”。
-
{ 命令; }:命令分組。括號中的命令被看作是一個命令。這在 BASH 的句法要求只可有一個命令,而你又找不到相應的可靠功能的時候很方便。
-
`命令`、$(命令):命令代換。(強烈推薦寫作后一種形式。)命令代換先執行其內部的命令,然后將整個 `…` 或 $(…) 替換為該命令的輸出。
-
(命令):子 shell 執行。這會在一個新的 bash shell 而非當前 shell 里面執行該命令。如果該命令有副作用(如更改變量),這些更改將不會影響當前 shell。
-
((表達式)):算術命令。在括號內,算符 +、-、* 及 /等都被看作數學算符。這可以用來進行如 ((a=b+7)) 的賦值,及 if ((a < b)) 這樣的測試。以后會詳述。
-
$((表達式)):與上面的類似,只是該表達式會替換為算術運算的結果。例:echo "The average is $(( (a+b)/2 ))"。
Char. |
Description |
" " |
Whitespace — this is a tab, newline, vertical tab, form feed, carriage return, or space. Bash uses whitespace to determine where words begin and end. The first word is the command name and additional words become arguments to that command. |
$ |
Expansion — introduces various types of expansion: parameter expansion (e.g. $var or ${var}), command substitution (e.g. $(command)), or arithmetic expansion (e.g. $((expression))). More on expansions later. |
'' |
Single quotes — protect the text inside them so that it has a literal meaning. With them, generally any kind of interpretation by Bash is ignored: special characters are passed over and multiple words are prevented from being split. |
"" |
Double quotes — protect the text inside them from being split into multiple words or arguments, yet allow substitutions to occur; the meaning of most other special characters is usually prevented. |
\ |
Escape — (backslash) prevents the next character from being interpreted as a special character. This works outside of quoting, inside double quotes, and generally ignored in single quotes. |
# |
Comment — an introduction of a # character begins a commentary that extends to the end of the line. Comments are notes of explanation and are not processed by the shell. |
[[]] |
Test — an evaluation of a conditional expression to determine whether it is "true" or "false". Tests are used in Bash to evaluate a number of conditions. More of this will be covered later. |
! |
Negate — used to negate or reverse a test or exit status. For example: ! grep text file; exit $?. |
>< |
Redirection — redirect a command's output or input. Redirections will be covered later. |
| |
Pipe — redirect output from a initial command to the input of secondary command. This is a method of chaining commands together. Example:echo "Hello beautiful." | grep -o beautiful. |
; |
Command separator — a representation of a newline. Used to separate multiple commands that are on the same line. |
{} |
Inline group — commands inside the curly braces are treated as if they were one command. It is convenient to use these when Bash syntax requires only one command and a function doesn't feel warranted. |
() |
Subshell group — similar to the above but where commands within are executed in subshell. Used much like a sandbox, if a command causes side effects (like changing variables), it will have no effect on the current shell. |
(()) |
Arithmetic expression — with an arithmetic expression, characters such as +, -, *, and / are mathematical operators used for calculations. They can be used for variable assignments like (( a = 1 + 4 )) as well as tests like if (( a < b )). More on this later. |
$(()) |
Arithmetic expansion — Comparable to the above, but the expression is replaced with the result of its arithmetic evaluation. Example:echo "The average is $(( (a+b)/2 ))". |
~ |
Home directory — the tilde is a representation of the home directory. When followed by a /, it means the current user's home directory; otherwise, a username will have to be specified (e.g. ls ~/Documents; cp ~john/.bashrc .). |
commend and arguments
命令與參數
BASH 從輸入中讀入命令(一般為終端或文件),它將自己讀入的每一行輸入都視為一條命令
BASH 在每一個空白字符(空格與 tab)處將每一個劃分為不同的詞語。BASH 第一個看到的命令為要執行的命令的名稱
在本文中,$在一行的開頭代表你的BASH提示。傳統上,一個shell提示符要么用$,%或#來表示結束。如果結尾$,這表明一個shell,與Bourne shell的兼容(如POSIX外殼或Korn shell中,或Bash)。如果結尾%,這表明一個C shell中(csh或tcsh); 本文不包括C shell
命令的分類
BASH 理解多種不同種類的命令:函數、內置命令、關鍵詞及可執行文件,別名
-
函數:BASH 的函數與別名有些相似,但功能更為強大。與別名不同,它們可用于腳本。函數包含 shell 命令,很像一個小腳本;它甚至能夠讀入參數與創建本地變量。當函數被調用時,其中的命令就會執行。函數部分將會在本指南稍后的部分講到。
-
內置命令:BASH 內置了一些基本命令,如 cd(change directory,更換目錄)、echo(寫出輸出)等等。你可以把它們看作是 BASH 已經提供的函數。
-
關鍵詞:關鍵詞與內置命令很相似,但主要的不同是對于它們適用不同的語句分析規則。比如,[ 是 bash 內置命令,而 [[ 則是 bash 關鍵詞。他們都是用來測試的命令,但由于 [[ 是一個關鍵詞而非一個內置命令
-
可執行命令:最后一種 BASH 可以執行的命令是可執行命令。也叫做外部命令或應用程序。可執行命令可以使用路徑名來執行。如果該可執行命令在當前目錄下,使用 ./myprogram。如果它在 /usr/local/bin 目錄下,使用 /usr/local/bin/myprogram
-
別名是一種縮短命令的方式。它們只能用于交互式 shell,不能用于腳本。用命令alias查看
腳本
腳本(script)可大致看作是文件里面的一系列命令。BASH 讀取該文件并按順序處理命令。它只有在當前命令運行結束時才會繼續運行下一文件,除非當前文件是異步(asynchronously)運行的(在后臺運行)。先不用擔心后一種情況——你以后會學到它是怎么回事。
實際上我們這篇指南中的每一個命令都既可運行于命令行,又可用于文本中。
編寫腳本很容易,只需創建一個新文件,并在頂部寫入下面的語句:
#!/usr/bin/env bash
該標頭(header,也叫做 hashbang 或 shebang)用于確保腳本在執行時會使用 BASH 作為解釋器。它工作的方式為,當內核執行非二進制文件時,它會查看該文件的第一行。如果這一行以 #! 開頭,內核就會使用該行來判定將把代碼交給哪一個解釋器。(也可以用其他方法——見下文。)#!必須在文件的開頭,前面不許有空格或空白行。你的腳本命令應該都放在下面不同的行中。
不要被網上使用 /bin/sh 的例子給欺騙了,sh 不是 bash。盡管 sh 的句法與 bash 的看起來很像,盡管大多數 bash 腳本可以在 sh 中運行,本指南中的許多例子只適用于 bash,如果使用 sh 的話會中斷或異常行為。
而且,不要給腳本一個 .sh 擴展名,它是沒用的,而且會誤導(因為這是 bash 腳本而非 sh 腳本)。
順便提一下,如果你使用 Windows 編寫腳本完全沒問題,但要盡可能地避免使用記事本(Notepad)來編寫腳本。微軟的記事本只能創建以 DOS 樣式作行尾的文件。這意味著你在記事本里面寫的每一行都會以兩個字符作結尾:一個回車和一個換行符。BASH 只會以換行符來判斷一行結束。結果,如果你不知道每一行還有一個回車符的話,它會給你帶來很大的難題(非常奇怪的錯誤提示信息)。盡可能地使用一個合適的編輯器,如 Vim、Emacs、kate、GEdit、GVIM 或 xemacs。否則,你必須在運行腳本之前移除回車符。
當腳本編寫完畢后,可以這樣調用它:
$ bash myscript
在本例中,我們執行 BASH并告訴它讀入我們的腳本。在我們這樣做的時候,#!只是一個注釋,BASH 會忽略它。
還有一種方式是,你可以給你的腳本一個可執行權限。這樣,你就可以把它當作一個應用程序來執行,而非手動調用 BASH。
$ chmod +x myscript $ ./myscript
以這種方式執行時,#! 一行就會告訴操作系統(OS)使用哪一個解釋器。系統會運行 /usr/bin/env,然后運行 bash。bash 再讀入我們的腳本。BASH 與上次一樣忽略標頭行。
一些人喜歡將一些腳本放在個人目錄下,還有人喜歡把腳本放在 PATH 變量指定的某個目錄下。大多數人都喜歡同時做這兩項。
下面是我建議你做的:
$ mkdir -p "$HOME/bin" $ echo 'PATH="$HOME/bin:$PATH"' >> "$HOME/.bashrc" $ exec bash
第一個命令會在你的家目錄下創建一個叫做 bin 的目錄。將含有命令的目錄命名為 bin 是一種習慣,即使有的命令是腳本而非預編譯(“二進制”)文件。第二個命令會為你的 .bashrc 文件添加一行語句,將我們剛創建的目錄加入 PATH 變量的最前端。每一個新的 BASH 實例都會檢查 bin 目錄下的可執行腳本。最后,第三行將當前的 BASH 實例替換為新的實例,新的實例會讀取 .bashrc 文件。
隱藏文件(DotFile)的更改不會立即生效。你必須采取一些步驟來重新讀入這些文件。在本例中,我們使用 exec bash 來替換當前運行的 shell。如果你愿意的話,你可以關閉當前終端并打開一個新的。BASH 會通過讀取 .bashrc 再次初始化。或者,你可以干脆在命令行上執行那一行(
PATH="$HOME/bin:$PATH"
)或者在正在運行的 shell 里面通過運行
source "$HOME/.bashrc"
手動處理 .bashrc 文件。
在任何情況下,我們都可以將腳本放到 bin 目錄下將之作為一個標準命令來執行了(我們不再需要在腳本的前面加上路徑,即在前幾例中是 ./ 的部分):
$ mv myscript "$HOME/bin" $ myscript
參數
參數是內存中的一種命名空間,可用來檢索與存儲信息。一般來說,它可以存儲字符串數據,也可用來存儲整數或數組。
參數分為兩類:變量和特殊參數
名稱規則:一個只包含字母、數字和下劃線,并以字母或下劃線開頭的詞語。也稱為標識符
賦值語句
name=value
請注意,= 號兩邊不可以有空格
,如果你這樣寫:
# This is wrong! $ varname = vardata
BASH 就不會知道你是要賦值。分析器將只能看到 varname 而看不到 =,然后又將 = 和 vardata 傳遞給 varname 作為參數。
為讀取變量中存儲的數據,我們使用參數展開(parameter expansion)。參數展開即把參數代換為它的值。意即該句式告訴 BASH 你想要使用變量的內容。此后,BASH 仍可以對結果進行額外操作。正確理解這個概念非常重要。因為這與在其他編程語言中處理變量的方式是很不一樣的!
讓我們使用這個例子來說明何為參數展開:
$ foo=bar $ echo "Foo is $foo"
當 BASH 要執行這段代碼時,它首先通過讀入要展開的變量($foo),并替換為 foo 的內容(bar),這段命令即變為:
$ echo "Foo is bar" Foo is bar
現在,BASH 就已準備好執行命令了。執行它時就會在屏幕上顯示這段簡單的句子。
需要理解的是參數擴展是用參數的內容代替 $參數。下面的例子會用到之前提到的參數劃分:
$ song="My song.mp3" $ rm $song rm: My: No such file or directory rm: song.mp3: No such file or directory
這里為什么不對?因為 BASH 將 $song 替換為其內容,即 My song.mp3;然后它又進行詞語劃分,直到這時才執行命令。相當于你鍵入了下面的語句:
$ rm My song.mp3
根據詞語劃分的規則,BASH 以為你用 My 和 song.mp3 來表示不同的文件。因為它們之間有空白字符且未引用。如何修改呢?我們要記住把每一個參數展開都置于引號內。
$ rm "$song"
參數:參數用于儲存可以使用符號或名稱來檢索的數據。
特殊參數和變量
讓我們在進入正式內容之前理順一下詞匯。我們知道有參數和變量。變量實際上只是一種參數——由名稱來表示的參數。不屬于變量的參數叫做特殊變量。相信你看了下面一些例子后就會再明白了:
$ # Some parameters that aren't variables: $ echo "My shell is $0, and has these options set: $-" My shell is -bash, and has these options set: himB $ # Some parameters that ARE variables: $ echo "I am $LOGNAME, and I live at $HOME." I am lhunath, and I live at /home/lhunath.
請注意:并不像 PHP/Perl/…參數不以 $- 開頭。你看到的 $- 符號只是會使其后的參數被展開。展開大體意為 shell 將參數替換為其內容。在這里,LOGNAME 是含有你的用戶名的參數(變量)。$LOGNAME 是以變量內容代替變量的表達式,對于我的情況是lhunath。
我覺得你應該明白了。下面對絕大多數特殊參數的概括:
-
0:包含腳本的名稱或路徑。并不總是可靠。
-
1 2 等:位置參數(Positional Parameters)包含傳遞到當前腳本或函數的參數(argument)。
-
*:
-
@:
-
#:展開為當前設置的位置參數的數量。
-
?:展開為最近一次在前臺完成的命令的退出值。
-
$:展開為當前 shell 的 PID 。
-
!:展開為最近一次在后臺運行的命令的 PID。
-
_:展開為最近一次運行的命令的最后一個參數。
下面是一些由 shell 自帶的變量:
常用 bash 環境變量
BASH_VERSION:包含描述 BASH 的版本的字符串。
-
HOSTNAME:包含計算機的主機名。根據計算機的設置,可短可長。
-
PPID:包含當前 shell 的父 shell 的 PID。
-
PWD:包含當前工作目錄。
-
RANDOM:每一次展開該變量時,都會產生一個 0 到 32767 之間的隨機數字。
-
UID:當前用戶的 ID 號碼。對于安全與認證用途不可靠,呃。
-
COLUMNS:終端中每一行的最長字符數值。(終端的字符寬度。)
-
LINES:終端的最大行數。(終端的字符高度。)
-
HOME:當前用戶的家目錄。
-
PATH:
-
PS1:包含描述 shell 提示符的字符串。
-
TMDIR:包含 shell 用于存儲臨時文件的目錄。
當然,你并不受限于這些變量,可以定義你自己的變量:
$ country=Canada $ echo "I am $LOGNAME and I currently live in $country." I am lhunath and I currently live in Canada.
注意我們是怎么給 country 這個變量指定 Canada 這個值的。記住等號兩邊不許有空格!
$ language = PHP -bash: language: command not found $ language=PHP $ echo "I'm far too used to $language." I'm far too used to PHP.
記住 BASH 不是 Perl 或 PHP。要記住展開的原理,以避免重大的麻煩。如果做不到的話,你寫出的腳本就會很危險,尤其是在用 rm 犯下這樣的錯誤:
$ ls no secret secret $ file='no secret' $ rm $file rm: cannot remove `no': No such file or directory
假如我們有兩個文件,no secret 和 secret。第一個文件里面沒什么有用的東西,但第二個則包含了拯救瀕于末日的世界的機密。你如此粗心,以至忘記引用 file 這個要展開的參數。BASH 展開了這個參數,其結果為 rm no secret。BASH 像平常一樣根據空白字符劃分參數,然后給了 rm 兩個參數:“no”和“secret”。結果,它沒有刪除文件 no,卻將文件 secret 給刪除了。該機密不見了!
變量類型
雖然 BASH 不是類型語言,它的確有不同類型的變量。這些類型描述了它們允許含有的內容。
類型信息存儲在 Bash 內部。
數組:delare -a 變量:該變量為一列字符串。
結合數組:delare -a 變量:該變量為字符串的結合陣列(bash 4.0 或更高)。
整數:declare -i 變量:該變量含有一個整數。為此變量賦值會觸發算術求值(Arithmetic Evaluation)。
聲明:delcare -x 變量:該變量被標記或聲明,會被其子進程繼承。
簡單說來,數組就是一列索引的(indexed)字符串。它可以把多個字符串儲存在一起而不用分界符,非常方便。
將變量定義為整數的方便之處在于要將其賦值或對其進行修改時省去一些句法。
$ a=5; a+=2; echo $a; unset a 52 $ a=5; let a+=2; echo $a; unset a 7 $ declare -i a=5; a+=2; echo $a; unset a 7 $ a=5+2; echo $a; unset a 5+2 $ declare -i a=5+2; echo $a; unset a 7
然而,declare -i 在實際中卻很少用。主要是因為它建立的行為讓每個忽略了 declare 語句的人看上去都很驚訝。大多數有經驗的 shell 腳本作者在進行算術運算時更愿意使用明確的算術命令(let or ((…)))。
使用 declare -a 來聲明數組的情況也很少見。直接寫 array=(…)就足夠了,Bash 就會知道該變量是數組。但結合數組是特例,我們必須明確地聲明:declare -A myarray。
參數展開(Parameter Expansion)
參數展開(Parameter Expansion)這一術語是指任何使一參數被展開(被其內容取代)的操作。最簡單的辨識方法是,參數展開是在參數前加一 $ 符號而達到的。在某些情況下,需要用花括號將參數的名稱括起來:
$ echo "'$USER', '$USERs', '${USER}s'" 'lhunath', '', 'lhunaths'
這個例子說明了簡單的參數展開(PE)是什么樣的。第二個 PE 的結果是個空字符串,因為參數 USERs 為空。我們沒有想讓 s 成為參數名稱的一部分。因為 Bash 無從知道有一個字面的 s 跟在參數的值后面,你需要用花括號來標記參數的開頭與結尾。這是我們在上面例子中的第三個 PE 中做的。
參數展開為我們提供了修改要展開的字符串的功能。這些操作非常方便:
$ for file in *.JPG *.jpeg > do mv "$file" "${file%.*}.jpg" > done
上面的代碼可以用來將所有以 .JPG 或 jpeg 的 JPEG 文件重命名為以 .jpg 為常見的擴展名。
表達式 ${file%.*} 從最末一個句點(.)開始截取掉所有字符。然后又在展開結果后附上了一個新的擴展名。
下面是對所有可用的 PE 技巧的歸納:
${參數:-詞語}:使用默認值。若“參數”未設置或為空,則代換為“詞語”。否則,則代換為“參數”。
${參數:=詞語}:指定默認值。若“參數”未設置或為空,則代換為“詞語“。
${參數:+詞語}:使用可選值。若“參數”未設置或為空,則無代換。否則代換為“詞語”。
${參數:偏距:長度}:子字符串展開。
${#參數}:代換為“參數”值的字符長度。
${參數#樣式}:使用“樣式”對“參數”從左至右進行匹配。結果為“參數”的值刪除最短匹配的部分。
${參數##樣式}:同上,但刪除最長匹配的部分。
${參數%樣式}:使用“樣式”對“參數”從右至左進行匹配。結果為“參數”的值刪除最短匹配的部分。
${參數%%樣式}:同上,但刪除最長匹配的部分。
${參數/樣式/字符串}:將“參數”的值中第一個匹配“樣式”的部分替換為“字符串”。
${參數//樣式/字符串}:同上,但替換所有匹配“樣式”的部分。
你會隨著經驗的增長掌握他們。他們比你想像得要方便得多。下面是一些簡單的例子幫助你入門:
$ file="$HOME/.secrets/007"; \ > echo "File location: $file"; \ > echo "Filename: ${file##*/}"; \ > echo "Directory of file: ${file%/*}"; \ > echo "Non-secret file: ${file/secrets/not_secret}"; \ > echo; \ > echo "Other file location: ${other:-There is no other file}"; \ > echo "Using file if there is no other file: ${other:=$file}"; \ > echo "Other filename: ${other##*/}"; \ > echo "Other file location length: ${#other}" File location: /home/lhunath/.secrets/007 Filename: 007 Directory of file: /home/lhunath/.secrets Non-secret file: /home/lhunath/.not_secret/007 Other file location: There is no other file Using file if there is no other file: /home/lhunath/.secrets/007 Other filename: 007 Other file location length: 26
記住 ${v##p} 和 ${v##p} 的不同。使用了兩個 # 字符意為該樣式變得很貪婪。 % 與此同理:
$ version=1.5.9; echo "MAJOR: ${version%%.*}, MINOR: ${version#*.}." MAJOR: 1, MINOR: 5.9. $ echo "Dash: ${version/./-}, Dashes: ${version//./-}." Dash: 1-5.9, Dashes: 1-5-9.
注意:你不可以同時使用多個 PE。如果你需要對于同一參數執行多個 PE,就需要使用多個語句:
$ file=$HOME/image.jpg; file=${file##*/}; echo "${file%.*}" Image
參考資料
The Bourne-Again Shell—–Chet Ramey
原創文章,作者:M21-郝建勛,如若轉載,請注明出處:http://www.www58058.com/55697