1. 程式人生 > 實用技巧 >Shell中的特殊符號(special characters)和含義 - 1

Shell中的特殊符號(special characters)和含義 - 1

之前寫過兩篇關於Bash語法的blog,分別是:

Bash特殊變數(Bash Special Variables)

Bash中的一些常用的陣列相關的特殊語法


個人感覺,想要通暢地讀懂bash指令碼,還差一個部分,那就是符號。


# 井號 (crosshatch/sharp/hash)

1. 井號開頭的語句被視為程式碼的註釋,不會被執行。

2. 在 #!/bin/bash 中,指令碼檔案開頭首行的#!會告訴系統這個檔案是一系列命令的集合,應該被髮送給#!後面指定的直譯器來執行。大概都有哪些直譯器呢?如下:

  • #!/bin/sh :Executes the script using the Bourne shell or a compatible shell, with path /bin/sh
  • #!/bin/bash :Executes the script using the Bash shell.
  • #!/bin/csh -f :Executes the script using C shell or a compatible shell.
  • #!/usr/bin/perl -T :Executes the script using perl with the option of taint checks
  • #!/usr/bin/env python :Executes the script using python by looking up the path to the python interpreter automatically from the environment variables


~ 波浪號(tilde)

Bash提供了一些用~開頭的變數,這些變數的展開叫做Tilde Expansions,也就是將這些縮寫轉換成它們代表的目錄名稱的過程。

1. 與$HOME一樣,代表當前使用者的home directory.

2. ~username, 代表其他某使用者的home directory.

3. ~+, 代表當前的工作目錄, 與$PWD一樣。

4. ~-,代表上一個工作目錄,與$OLDPWD一樣。

5. ~1, ~2, ~-1, ~-0 分別代表目錄棧中的編號為1的,編號為2的,編號為倒數第2的,倒數第一的目錄。相當於‘dirs +1’, ‘dirs +2’, ‘dirs -1’, ‘dirs –0’.

下面展示了一個例子。關於操作目錄棧的具體內容,請看這裡


; 分號(semicolon)

分號用來分隔兩個命令,比如命令 echo a; echo b 就是告訴bash,echo a 和 echo b 是兩個不同的命令,需要一個接一個地分開執行。參考這裡


;; 雙分號(double semicolon)

雙分號;; 僅用在case結構中,標誌一個選項的結束,類似於C語言中的break。

1
2
3
4
case $answer in
  yes) echo 'yay!';;
  no) echo 'boo!';;
esac


. 點號(dot)

一個 dot 代表當前目錄,兩個 dot 代表上層目錄。如果檔案名稱以 dot 開頭,該檔案就屬特殊檔案,用 ls 指令必須加上 -a 選項才會顯示。除此之外,在 regular expression 中,一個 dot 代表匹配一個字元。


‘string' 單引號 (single quote)
被單引號用括住的內容,將被視為單一字串。在引號內的代表變數的$符號,沒有作用,也就是說,$符號被視為一般符號處理,防止任何變數替換。


“string” 雙引號 (double quote)
被雙引號用括住的內容,將被視為單一字串。它防止萬用字元擴充套件,但允許變數擴充套件。這點與單引數的處理方式不同。


`command` 反引號 (backtick)

反引號不是一種引號,它有特殊的意義。任何被反引號括起來的東西都會在主命令執行之前先被shell求值(或執行),並且反引號括起來的東西的求值(或執行)的輸出會被主命令使用到。

在前面的單雙引號,括住的是字串,但如果該字串是一列命令列,會怎樣?命令會執行麼?

答案是不會執行。要處理這種情況,我們得用反引號。


在反引號內的 date +%F 會被視為指令,執行的結果會帶入 $fdv 變數中。


下面的例子裡,先建立一個名為lucky的user,可以看到他的uid為1001.

檢視當前使用者的資訊,名為yunlong的使用者的uid為1000. 可以看到/home/lucky 是lucky使用者的home directory,owner是lucky。

然後,我們利用命令 chown `id -u` /home/lucky, 把/home/lucky的owner換成了yunlong。注意這裡對於反引號的使用。

關於反引號的更多資訊,看這裡


, 逗號 (comma)

逗號可以有下面這些用途:

1. 括號擴充套件(brace expansion)

tee 命令的作用是從標準輸入讀取(standard input)之後,將讀取來的資訊同時寫入標準輸出(standard output)和一個或多個檔案中。詳情見這裡


2. 在let語句中,或者在與其作用等價的(())構造中分隔算數操作。


3. 可以在for語句中,分隔不同的index變數,避免在for迴圈體中進行操作。


4. 逗號還可以用在Bash4中,把字串變成小寫。

關於逗號在bash中的用途,原文看這裡


/ 斜槓 (forward slash)

在路徑表示時,斜槓代表目錄。通常單一的斜槓 / 代表 root 根目錄的意思;在四則運算中,斜槓代表除法的符號。


\ 反斜槓(backslash/escape)

在互動模式下的escape符號,有幾個作用:

1. 放在指令前,有取消 aliases 的作用;

# type rm
rm is aliased to `rm -i'
# \rm .\*.log
上例,作者在 rm 指令前加上 escape 字元,作用是暫時取消別名的功能,將 rm 指令還原。

2. 轉義字元,放在特殊符號前,則該特殊符號的作用消失;

3. 放在指令的最末端,表示指令連線下一行。


| 豎線 bar/pipe/vertical bar

pipeline 是 UNIX 系統,基礎且重要的觀念。連結上個指令的標準輸出,做為下個指令的標準輸入。

善用這個觀念,對精簡 script 有相當的幫助。


! 歎號 exclamation mark/bang

通常它代表反邏輯的作用,譬如條件偵測中,用 != 來代表”不等於”

# if [ "$?" != 0 ] ; then echo “Executes error”; elif then echo "Executes successfully"; fi;

這個例子裡,除了展示了歎號的用法,還包含了如何把if語句寫在一行裡的方法,還有一個利用上面講過的反斜槓把一個一行的if語句寫成多行的方法。

在正則表示式裡,可以使用!字元在正則表示式之前對其求反。也就是說,僅當字串與表示式的其餘部分不匹配時,才認為字串已匹配。


注意,網上的很多名為《shell指令碼中一些特殊符號》的文章裡,在講歎號是給出的例子裡使用歎號是不正確的。

應該使用^符號,來對字元範圍取反。如下:


: 冒號 colon

在 bash 中,這是一個內建指令, 這個指令”什麼事都不幹(no-op)”,但返回狀態值 0 (Bash裡返回值為0表示正常執行結束,非0表示出了問題)。

比如,if語句利用上冒號可以寫成這樣:

# if [ "$?" != 0 ]; then :; else echo "Execute successfully"; fi

關於冒號在Bash中的應用,具體可以看這裡


? 問號 (question mark)

在檔名的匹配中,用作單字元的萬用字元。注意,問號代表的是隻有一個字元的萬用字元,數字和字母都可以匹配。


* 星號 (asterisk/star)

星號是bash中的任意字元序列萬用字元,包括沒有字元的情況都可以匹配。


** 雙星號 (double-asterisk)

雙星號(**)在多個段中嘗試匹配零個或多個字元,它用於對巢狀目錄中的檔案進行通配。

舉例:

Tests/**/*.js

這裡,匹配的檔案被限制在Test目錄中。會被匹配的檔案包括Tests/HelloWorld.js, Tests/UI/HelloWorld.js, Tests/UI/Feature1/HelloWorld.js

更多資訊,看這裡


另一個就是冪運算。


$ 美元符號(dollar sign)
變數替換(Variable Substitution)的代表符號。 一個變數名字前面帶上美元符號,表明這是個變數。可以用美元符帶上這個變數的名字對其進行引用。
另外,在 Regular Expressions 裡被定義為匹配行尾 (end-of-line)。

還有很多其他特殊變數是用美元符號開頭的,參考這裡


${} 變數擴充套件(variable substitution)

${ }用於變數替換。一般情況下,$var 與${var} 並沒有啥不一樣。但是用 ${ } 會比較精確的界定變數名稱的範圍。

$ AB=string0

$ A=string1
$ echo $AB

string0
# 原本是打算先將 $A 的結果替換出來,然後再補一個 B 字母於其後.

# 但在命令列上,真正的結果卻是隻會提換變數名稱為 AB 的值出來…
# 若使用 ${ } 就沒問題了:
$ echo ${A}B

string1B

詳情看這裡這裡


$() 命令替換 (command substitution,與一對反引號相同)

在 bash shell 中,$( ) 與` ` (反引號) 都是用來做命令替換用(command substitution)的。

例如version=$(uname -r)和version=`uname -r`都可以是version得到核心的版本號。

1. 反引號的命令替換基本上可在全部的 unix shell 中使用,若寫成 shell script ,其移植性比較高。但是,反引號容易打錯或看錯。

2. $()並不是所有shell都支援。


$(()) 和 $[] 算數擴充套件(arithmetic expansion)

它們是一樣的,都是進行數學運算的。支援+ - * / %:分別為 “加、減、乘、除、取模”。但是注意,bash只能作整數運算,對於浮點數是當作字串處理的。

二者的區別是$[]已經是淘汰的,棄用的語法了,現在已經完全被$(())替代了。


() 小括號(parentheses) 指令群組 (command group)

小括號之內的一系列命令,會在一個新建立的subshell中執行。由於小括號內的命令是在subshell中執行的,所以其內部的變數賦值在執行之後不會持續。

Placing a list of commands between parentheses causes a subshell environment to be created (see Command Execution Environment), and each of the commands in list to be executed in that subshell. Since the list is executed in a subshell, variable assignments do not remain in effect after the subshell completes.


{} 花括號(braces)指令群組 (command group)

花括號之內的一系列指令會在當前的shell的上下文中執行。不會建立subshell。 列表之後的分號(或者換行)是必須要有的。

Placing a list of commands between curly braces causes the list to be executed in the current shell context. No subshell is created. The semicolon (or newline) following list is required.

具體解釋參考這裡


(()) 雙括號 (Double-Parentheses)

跟let命令一樣,雙括號允許在其中進行算數擴充套件和求值。 最簡單的一個例子, a=$((5+3)), 會將5+3的結果賦值給a。 雙括號還是一個在bash中使用的C語言風格的操作變數的方法,舉個例子 ((var++)).

詳情看這裡


[] 單方括號 (Single Square Brackets)

單方括號是內建命令test的另一種形式,方括號括住的命令會被驗證其是否為真。 注意,空字串為false,非空字串為true。

既然是test命令的另一種形式,那麼test命令的各種選項就也是可以應用的。

具體看這裡


[[]] 雙方括號 (Double Square Brackets)

[[ 是新的,提高版的[, 雙方括號是個關鍵字,不是一個程式。雙方框更好用,比較見下表。

英文原文如下:

[ : test implements the old, portable syntax of the command. In almost all shells (the oldest Bourne shells are the exception), [ is a synonym for test (but requires a final argument of ]).

[[ : is a new, improved version of it, and it is a keyword rather than a program. This makes it easier to use

具體看這裡這裡


&& 邏輯與, || 邏輯或 (Logical Operators)

雙&&符號在Bash中意思是邏輯與(AND),並且可以用來分隔幾個順序執行的命令。

舉例:

# cd /root/ && echo "I've got root"

具體看這裡


& 後臺工作 (Single Ampersand)

在Bash中, &是控制字元,放在命令的末尾,其作用是讓命令在後臺執行,也就是說在另外的一個subshell裡,以非同步的方式,如同一個job一樣的執行。當前的Shell會立即回到等待使用者輸入的狀態,並且返回值為0.

舉例:

# shell會給出後臺執行命令的process ID (PID), 這個ID會儲存在$! 變數中。

$ ./myscript.py &
[1] 1337


# 後面可以通過$!變數對該程序進行引用。
$ echo $!
1337


# 一旦生成了新的後臺程序,這個程序會出現在job列表中。

$ jobs
[1]+ Running ./myscript.py &


# foregroud命令可以把這個程序拿回前臺。

fg

具體看這裡


+ 加號, -減號, * 乘號, / 除號

比較簡單,不說了。


= 等號(賦值), == 雙等號 (判斷相等), != 不等於號

簡單,也不說了。


^ 折音號 (circumflex/caret)

這個符號在正則表示式中,代表行的開頭位置,在[]中也與!(歎號)一樣表示非,即求反。


>, >>, <, <<, >&, <&, <<tag, 2>, 2>>, 2>&1 輸入輸出重定向

這裡符號比較多,一個一個簡介吧。

1. command > file --- 將輸出重定向到file

2. command < file --- 將輸入重定向到file,確切的說是把file中的內容拿來作為command的輸入。

3. command >> file --- 將輸出以追加的方式重定向到file

4. n >& m --- 將輸出檔案m和n合併

5. n <& m --- 將輸入檔案m和n合併

6. << tag --- 將開始標記 tag 和結束標記 tag 之間的內容作為輸入。


輸入重定向舉例:

一般情況下,每個 Unix/Linux 命令執行時都會開啟三個檔案:

  • 標準輸入檔案(stdin):stdin的檔案描述符為0,Unix程式預設從stdin讀取資料。
  • 標準輸出檔案(stdout):stdout 的檔案描述符為1,Unix程式預設向stdout輸出資料。
  • 標準錯誤檔案(stderr):stderr的檔案描述符為2,Unix程式會向stderr流中寫入錯誤資訊。

預設情況下,command > file 將 stdout 重定向到 file,command < file 將stdin 重定向到 file。

如果希望 stderr 重定向到 file,可以這樣寫:

$ command 2>file


如果希望 stderr 追加到 file 檔案末尾,可以這樣寫:

$ command 2>>file


2 表示標準錯誤檔案(stderr)。如果希望將 stdout 和 stderr 合併後重定向到 file,可以這樣寫:

$ command > file 2>&1
$ # 或者
$ command >> file 2>&1


更多資訊,看這裡




參考資料

=============

Shell中的特殊符號和含義簡明總結

https://blog.csdn.net/wejfoasdbsdg/article/details/53289589

shell指令碼中一些特殊符號

https://www.cnblogs.com/xuxm2007/archive/2011/10/20/2218846.html

(#!/bin/bash ) What exactly is this ?

https://medium.com/@codingmaths/bin-bash-what-exactly-is-this-95fc8db817bf

目錄堆疊

https://wangdoc.com/bash/stack.html

How to Create Users in Linux (useradd Command)

https://linuxize.com/post/how-to-create-users-in-linux-using-the-useradd-command/

The Magic ~: Bash Tilde Expansion with 5 Examples

https://www.thegeekstuff.com/2010/06/bash-tilde-expansion