Linux命令行基礎,關於Bash需要知道的一些常識

介紹

Linux越來越重要,對於開發者來說學習必要的bash技術必不可少,對運維來說更是如此。學習shell可以參考很多學習材料和圖書,比如ChinaUnix論壇網中人大哥總結的《Shell十三問》的帖子,《ABS 指導》(《Advanced Bash-Scripting Guide》),《linux shell腳本攻略》等大家可以參考。本文蟲蟲給大家一些常用的shell技巧分享給大家,希望可以對大家有所幫助。

Linux命令行基礎,關於Bash需要知道的一些常識

引導順序

首先我們要說下bash腳本的啟動,Linux中系統初始化都是按照一定順序加載各個文件初始化腳本(Shell腳本)。腳本所在文件啟動順序很重要,下面這個圖顯示了Linux系統個初始化腳本和環境變量的加載順序,包括了bash、sh和zsh等常見的shell。

Linux命令行基礎,關於Bash需要知道的一些常識

bash的加載遵循上圖,從上到下順序執行加載,尤其要注意non-login、非交互(上面藍色線)執行,它不會加載很多腳本,比如/etc/profile(總profile),/etc/bash.bashrc(總basrc),和個人的.profile, .bash_profie,.bash_login和.bashrc等。所以如果你腳本處於這樣環境下(比如cron腳本),你就要在腳本中自我設置一些環境變量。這樣就可以避免由於環境變量導致的一些莫名其妙的的問題,比如寫的好腳本為什麼登陸就可以執行,放到cron裡面自動執行就不行。

輸入/輸出和重定向

我們知道Linux下一切皆文件,包括硬件設備,其中三個特殊的文件句斌,標準輸入(stdin)、標準輸出(stdout)和標準錯誤(stderr),其句柄號為三個整數0,1和2。

< :用於重定向改變標準輸入(stdin)。

0< :0< 和

<< :輸入一段文本,直到讀到<

> :用於重定向改變標準輸出(stdout)。

1> :1> 和>一樣 ,> 默認的FD為1。

2> :改變標準錯誤(stderr)。

>> :重定向輸出到文件,追加到文件末尾。

set -o noclobber :設置 > 不能覆蓋已經存在的文件。

set +o noclobber :這支 > 可以覆蓋已經存在的文件。

>| :設置set -o noclobber後,使用 >| 可以臨時覆蓋已經存在的文件。

/dev/null 和 /dev/zero

這兩個文件是兩個比較特殊設備。/dev/null,或稱空設備,它會丟棄一切寫入它的數據,但是讀取它則會拋出錯誤。在shell中常用它來表示放棄執行的結果(或者錯誤),這樣只關注於程序執行,比如在cron中的任務,輸出和錯誤都沒有意義。

command > /dev/null

可以將stdout、stderr都重定向輸出到/dev/null:

command > /dev/null 2>&1 或者

command &> /dev/null

command >& /dev/null

/dev/zero 設備用於提供無限制的空null內供讀取。當讀取該設備(文件)時候,它會提供無限的空字符 null。我們可以從/dev/zero 讀取任意數量的 null 字符。和 /dev/null 不同,/dev/zero主要用於作為null數據源供讀取,當然/dev/zero也支持寫入,可以用做數據黑洞,。

/dev/zero主要用途是提供字符流來初始化數據存儲,也就是使用空字符覆蓋目標數據。還可以用來一個特定大小的空白文件。

比如用空白文件覆蓋分區,刪除分區的數據可以用(慎用!):

dd if=/dev/zero of=/dev/<destination>

創建一個一個1M的文件,可以使用

dd if=/dev/zero of=cc count=1024 bs=1024

``和$()

反引號(Tab鍵上面的鍵)和$(),兩者基本上類似,在shell中都表示執行命令。

echo`ls`

echo $(ls)

最常用的用法是將一些命令結果輸出傳遞個其他命令作為參數。比如要查詢當前目錄和子目錄下所有PHP中的包含"eval"函數(似疑木馬),可以用下面命令:

grep eval `find -type f -name "*.php"`

兩者主要的區別在於嵌套更簡單。

對``來說要實現嵌套就要增加很多反斜槓來轉義,比如:

echo `echo \\`echo \\\\\\`echo hello,Chongchong\\!\\\\\\`\\``

而用$()就簡介的多了:

echo $(echo $(echo $(echo hello,Chongchong\\!)))

這是個很有意義的操作符,他有點類似於$(),而對括弧中命令的輸出重用。但是在

比如我們要執行下面一些例子

grep keys file1> /tmp/a

grep keys file2> /tmp/b

diff /tmp/a /tmp/b

diff

引用

bash引用比較複雜,我們先從簡單的開始。

首先,引號中的變量:

A='123'

echo "$A"

echo '$A'

這個結果很簡單,雙引號會解釋變量,輸出為其值123,而單引號不會解釋,輸出為$A。

下面三個echo 都會打印啥呢?

mkdir -p tmp

cd tmp

touch a

echo *

echo "*"

echo '*'

Bash中還有很多引用方式的快捷操作符,其中一些可以極大的提高我們的工作效率

!$

表示重複最後一個命令的最後一個參數。如果你正在處理一個文件,通過該操作符可以在命令後重新引入參數,從而節省大量重複輸入。

grep xxx /long/path/to/some/file/or/other.txt

vim !$

!:1-$

該操作符,更有意思,它獲取上一個命令的所有參數並將它們引入。所以:

grep isthere /long/path/to/some/file/or/other.txt

egrep !:1-$

fgrep !:1-$

!表示'查看上一個命令',:是一個分隔符,1表示'取第一個單詞', -表示範圍'直到',$表示'最後一個單詞'。

我們可以用!*實現同樣的目的。但是基於上面規則,我們就可以隨意定製參數的範圍了。比如如使用!:2-3。

:h

如果把該操作符放在一個文件名後面,它會刪除文件夾以外的文件名稱。比如:

grep XXX /long/path/to/some/file/or/other.txt

cd !$:h

通配符和正則表達式

通配符合正則表達式中都會用*,看起來很相似,但它們差異很大

請解釋下面這個命令:

rename -n 's/(.*)/HEAD$1/' *

上面兩個星號表達意思完全不同:

第一個由於用單引號括住,shell不會對其解釋,作為參數都提交給rename命令。rename會對其解釋 ,而s/(.*)/HEAD$1/是一個正則替換。在正則表達式中 *表示對前面字符的0個或者多個模式。.表示一個字符,所以.*表示匹配所有內容;()表示對括住內容進行引用,在替換後面部分用\\1或者$1表示它。

第二個會被shell解釋,*在shell中表示通配符,表示當前工作文件夾中所有文件的列表替換。

所以整個命令表示將文件下所有文件名都增加上HEAD 前綴。請嘗試下面兩個命令:

ls *

ls .*

第二個看起來更像是一個正則表達式,但是實際是什麼樣的呢?請你嘗試對輸出解釋下。

條件語句,[和[[

請嘗試執行一下語句會輸出什麼?

if grep XXX /dev/null

then

echo Chongchong

else

echo lo

fi

grep的返回代碼使得像這樣的代碼更直觀地作為其作為退出狀態解釋時候帶來的副作用。

下面兩個語句的輸出呢?

if [ $(grep XXX /dev/null) = '' ]

then

echo -n Chongchong

else

echo -n lo

fi

if [[ $(grep XXX /dev/null) = '' ]]

then

echo -n Chongchong

else

echo -n lo

fi

[是測試的原始形式,然後[[引入了更靈活和直觀的方式。在上面的第一個if塊中,if語句因為$(grep XXX /dev/null)被評估為空,導致這種比較:[=''],這會拋出錯誤,針對這種情況就需要使用[[]]來避免這種異常,還有一個常用的技巧是使用:

if [x$(grep XXX /dev/null)='x']

所以,如果命令沒有返回任何內容,它仍然可以正常運行,而不會拋出異常,中斷執行。

退出狀態碼

我們知道每次shell執行,推出是都會給shell一個狀態碼。

如果命令執行正常,則會返回給shell狀態碼0。如果不成功,則會得到一個非零代碼,用來表具體錯誤的種類,其中 1表示"一般錯誤",還有其他更多信息,可以在程序中具體定義。狀態碼,可以通過$?查看。但有些命令會設置有特殊設置比如grep,請嘗試下面的命令:

grep XXX /dev/null

echo $?

grep中使用退出代碼來指示它是否匹配。

set 語句

Bash中也提供了可配置的選項,可以即時設置環境變量等。

set -e

如果任何命令返回非零退出狀態,則從腳本退出。

set -X

會輸出在運行時運行的命令。

所以腳本可能會使用這樣的開頭:

#!/bin/bash

set -e

set -x

grep XXX /dev/null

echo $?

getopts

如果要寫一個較大的程序,涉及的輸入比較複雜,則需要使用getopts來簡化你的工作,用他來幫你做輸入和參數處理的工作,比如下面一個例子:

Linux命令行基礎,關於Bash需要知道的一些常識

getopts後面跟的是參數列表字符串,每個字母表示一個選項,帶:選項表示選項還會有值,比如上面例子中對應的-j /home/soft/java 和-m /home/soft/maven 。而getopts字符串中沒有跟隨:的字母就是開關型選項,不需要指定值,等同於true/false,只要帶上了這個參數就是true。

getopts是shell內部命令,可以在shell腳本中直接使用,同時shell也支持一個外部命令getopt有很多發行版本也都自帶。詳細可以搜索參考具體文檔,本文不在贅述。

總結:

拋磚引玉,本文中列出了shell中一些常見的技巧和容易混淆的知識點,希望能對大家有所幫助,如果大家有更好的想法技巧也可以一起分享出來,一起參考學習。

"


分享到:


相關文章: