Linux系統介紹(五)常用命令

cat命令

很多時候我們通過cat命令來查看文件內容,它會將文件的所有內容顯示出來。當然,cat也可以通過管道接收數據,它主要完成的是將從管道接收的輸入導到輸出。

more跟less命令

有時候用cat命令來顯示一個較大的文件并不方便,整個文件內容一次性顯示出來簡直就是刷屏了。如果需要一頁頁的顯示內容,可以使用more或者less命令,這兩個命令會以分頁的形式顯示文件內容,至于使用哪個命令完全看個人習慣了。此外,這兩個命令不僅可以分頁顯示,而且在分頁模式下,你可以用快捷鍵方便的瀏覽及搜索:

* 按`d`下翻頁
* 按空格下翻頁
* 按回車下移一行
* 按`/`進入搜索模式,輸入要搜索的關鍵字,按回車搜索。
* 按`n`搜索下一個
* 按`q`退出查看

tee命令

tee命令一般從管道接收數據,這點與cat類似,將stdin導到stdout。不同的是,tee同時還可以指定一個文件作為輸出。這點非常有用,有時候我們想一般看到命令的輸出,同時又希望將輸出保存到文件中,這時候用tee最為合適。

# date | tee time.log
Mon Nov 20 14:05:02 EST 2017
# cat time.log
Mon Nov 20 14:05:02 EST 2017

date命令

date命令用來顯示時間跟時區,比較常見的用法有:

  • 默認顯示

    # date
    Sun Nov 19 20:08:21 EST 2017
    # date -u
    Mon Nov 20 01:08:28 UTC 2017
    

    其中,-u參數表示顯示UTC標準時間,即時區為0的時間。

  • 指定顯示格式

    除了默認輸出,我們也可以指定顯示的格式:

    # date +'%A %d-%m-%Y UTC %:z'
    Sunday 19-11-2017 UTC -05:00
    

    date支持非常多元化的格式,具體可以參考這里

  • 顯示當前時間的秒數

    通常,在計算當前時間的秒數的時候,我們通常會以Unix Epoch Time為基準,用date命令可以非常方便的顯示當前時間的秒數:

    # date +%s
    1511141040
    # date +%s --date='2017/11/19 09:56:00'
    1511103360
    

    其中,也可以通過參數--date指定時間來計算。反過來,如果我們知道了時間的秒數,需要顯示其相對于Unix Epoch Time的時間,可以這么做:

    # date; date +%s
    Mon Nov 20 10:33:07 EST 2017
    1511191987
    # date --date=@1511191987
    Mon Nov 20 10:33:07 EST 2017
    
  • 時間偏移計算

    有時候需要知道多少天前是什么時間,這時候需要用到時間偏移計算了:

    # date --date='100 seconds ago'
    Sun Nov 19 20:35:44 EST 2017
    # date --date='100 hours ago'
    Wed Nov 15 16:37:28 EST 2017
    # date --date='100 days ago'
    Fri Aug 11 21:37:34 EDT 2017
    

    date命令可以識別多種時間偏移寫法,除了示例中的,還有minutes months years等,當然,也可以這樣寫:

    # date --date='+ 1000 seconds'
    Sun Nov 19 20:56:28 EST 2017
    # date --date='- 1000 seconds'
    Sun Nov 19 20:23:41 EST 2017
    # date --date='2017-11-19 00:00:00 + 1000 seconds'
    Sat Nov 18 09:00:01 EST 2017
    

    直接用+或者-表示以后或者以前的時間,也可以指定某個時間點然后偏移。

  • 設置時間

    當然,你也可以通過date命令來設置時間:

    # date
    Sun Nov 19 20:44:11 EST 2017
    # date --set='Sun Nov 19 20:44:30 EST 2017'
    Sun Nov 19 20:44:30 EST 2017
    # date
    Sun Nov 19 20:44:31 EST 2017
    

    其中--set也可以簡寫為-s,時間格式非常靈活:

    # date -s '2017/11/20 10:19:50'
    Mon Nov 20 10:19:50 EST 2017
    

cal命令

我們用date可以顯示時間,同時咱們還可以通過cal命令來顯示日歷:

# cal
    November 2017
Su Mo Tu We Th Fr Sa
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30

當然,你也可以指定要顯示的日期,比如1949年10月的日歷:

# cal 10 1949
    October 1949
Su Mo Tu We Th Fr Sa
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

time命令

有時候我們需要知道一個命令運行了多少時間,這時候我們可以用time命令來計時:

# time sleep 1

real    0m1.018s
user    0m0.001s
sys     0m0.002s

其中sleep 1用來睡眠1秒,real表示實際用了多少時間,user表示在用戶態花了多少時間,sys則表示在內核花了多少時間。詳細可以參考這篇問答

wc命令

wc命令是Word Count的簡稱,顧名思義就是用來統計單詞的。

# cat test.log
Nov 17 00:27:20 traffic-base1 named[1212]: managed-keys-zone: Unable to fetch DNSKEY set .: timed out
# cat test.log | wc -w
14

參數-w表示統計單詞數,這里的單詞實際上指的是被空格分開的字符串。下面列舉出wc命令的有關參數:

參數 說明
-w 統計多少單詞
-l 統計多少行
-c 統計有多少個字節
-m 統計有多少個字符
-L 統計長度最長的行的長度

注意,這里的字節跟字符的差別,在英文中基本上是一樣的,但是在多字節語言中,其意義就不一樣了:

# echo '你好' | wc -c
7
# echo '你好' | wc -m
3

find命令

find命令用來查找文件或目錄,這又是一個非常強大的且常用的命令,這里只介紹幾種常見的用法:

  • 根據文件名查找

    這是基本且常見的用法:

    # find . -name "test*"
    ./test.log
    ./test2
    

    示例中表示在當前目錄(用.表示)下包括其子目錄,查找文件名以test開頭的文件。默認情況下,find命令是大小寫敏感的,如果需要忽略大小寫,則可以改用參數-iname

  • 根據類型查找

    可以根據類型查找文件:

    # find . -type f
    ./Test1
    ./test.log
    ./test2
    

    當然,也可以同時根據類型跟文件名一起查找:

    # find . -type f -name "test*"
    ./test.log
    ./test2
    

    f表示文件,如果是查找目錄的話則用d

  • 根據時間查找

    find命令還可以根據時間來查找文件目錄,其中一個用法如下:

    find . -newer base_file
    

    表示在當前目錄下查找比base_file文件更新的文件或者目錄。此外,find還可以根據文件的atime, ctime, mtime來查找文件,如下,根據修改時間來查找:

    # date
    Fri Nov 17 20:34:52 EST 2017
    # ll
    total 20
    drwxr-xr-x.  2 root root   45 Nov 17 02:46 ./
    dr-xr-x---. 48 root root 8192 Nov 17 02:46 ../
    -rw-r--r--.  1 root root    0 Nov 16 00:17 Test1
    -rw-r--r--.  1 root root   34 Nov 17 02:46 test2
    -rw-r--r--.  1 root root  102 Nov 17 00:30 test.log
    # find . -mtime -1
    .
    ./test.log
    ./test2
    

    其中-mtime表示根據修改時間查找,-1表示最近一天。find支持的時間查找總結如下:

參數 說明
-mtime 根據修改時間,也就是ls -l顯示的時間
-atime 根據訪問時間,也就是ls -lu顯示的時間
-ctime 根據狀態改變的時間,也就是ls -lc顯示的時間

時間值的表示說明:基準+0表示一天前,-1.5表示最近1.5天,+1.5表示2.5天前

  • 邏輯查找

    find支持與或非邏輯的查找,比如查找所有C++的源文件,實際上需要找出后綴為.cpp.h的文件,需要用到find的邏輯或的查找:

    find . -name "*.cpp" -o -name "*.h"
    

    其中-o-or的縮寫,用來表示邏輯或的關系,而-name "*.cpp"-name "*.h"為表達式,構成了EXP1 or EXP2的關系,只要文件或者目錄滿足其中一個表達式就會輸出。find支持的邏輯關系如下:

邏輯 參數 說明
-a -and的縮寫,邏輯與的關系,如find . -type f -a -name "*.log"
-o -or的縮寫,邏輯或的關系, 如find . -name "*.cpp" -o -name "*.h"
! -not的縮寫,邏輯非的關系, 如find . ! -name "*.cpp"

此外,find命令還有一個非常重要且常見的用法,就是在找到文件后執行某個命令,改用法如下:

find . -name "*.log" -exec rm {} \;

表示刪除當前目錄包括子目錄中以.log為后綴的所有文件。其中,-exec表示在找到后需要執行命令,而命令為rm {} \;,實際上此命令就是一般的shell命令,其中{}用來指代找到的文件或目錄,這里;必須轉義,因為需要傳遞給find本身,如果不轉義,則會直接被shell解析使用了。每找到一個文件或目錄,都會執行指定的命令,其中{}部分以文件路徑替代。如果需要只執行一次命令,而把所有找到的文件作為參數傳遞給該命令,則需要用+替代\;,如:

find . -name "*.log" -exec rm {} +

假定找到的文件有test.logtest1.log,用\;的方式相當于執行兩次:rm test.logrm test1.log;如果使用+則只有一次命令rm test.log test1.log

sort命令

顧名思義,這個命令就是用來排序的。

# head -n 5 /etc/passwd | sort
adm:x:3:4:adm:/var/adm:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
root:x:0:0:root:/root:/bin/bash

默認情況下,sort命令以字典順序對每行進行排序,如果不帶參數,會將整行作為一個字符串進行比較。當然,你也可以指定以第幾列進行排序:

# head -n 5 /etc/passwd | tr : " " | sort -k 3
root x 0 0 root /root /bin/bash
bin x 1 1 bin /bin /sbin/nologin
daemon x 2 2 daemon /sbin /sbin/nologin
adm x 3 4 adm /var/adm /sbin/nologin
lp x 4 7 lp /var/spool/lpd /sbin/nologin

這里先將:換成空格(關于tr命令,請參照本文關于tr命令的章節),然后以-k為參數,指定以第三列進行排序。下面列舉其常用的一些參數:

參數 說明
-k 以第幾列排序,列以空格為分隔
-r 默認sort以升序輸出,-r參數則可以以降序輸出
-n 在指定第幾列的時候,可以強制sort把列的值以數字值進行排序,如下面的例子
-t 默認情況下認為列是以空格為分隔,-t參數則可以指定分隔符,這樣,上面的例子其實可以直接寫成sort -t : -k 3
-f 忽略大小寫
-u 如果排序后出現重復的行,加上這個參數將只顯示一行
# cat test2
10
3
5
3
# cat test2 | sort
10
3
3
5
# cat test2 | sort -n
3
3
5
10
# cat test2 | sort -n -u
3
5
10

uniq命令

uniqunique的簡寫,用來消除sort排序后重復的行,即相當于sort命令中的-u參數。但是,uniq不僅可以消除重復行,它還可以顯示分別重復了多少行:

# cat test2 | sort -n | uniq -c
      2 3
      1 5
      1 10

還有些常用的參數如下:

參數 說明
-i 忽略大小寫
-d 只打印有重復的行,每組一個,如果要打印組內所有的,則用-D
-u 只打印沒有重復的行
-f 比較的時候,忽略前面的N列
-s 比較的時候,忽略前面的N個字符

od命令

od可以用來顯示二進制文件:

# cat test2
abcdefg
1234567
# od -t x1 test2
0000000 61 62 63 64 65 66 67 0a 31 32 33 34 35 36 37 0a
0000020

當然,也可以直接顯示字符:

# od -c test2
0000000   a   b   c   d   e   f   g  \n   1   2   3   4   5   6   7  \n
0000020

當然,如果僅僅只是顯示二進制內容,還可以使用hexdump命令了。

壓縮類命令

tar命令

在Linux中,用的最多的壓縮命令就是tar命令了,在介紹其用法之前,需要清楚幾個概念:

  • 存檔文件(Archive File)

    存檔文件用來打包多個文件成一個文件,以方便在網絡上傳輸。請注意,打包成的文件并沒有被壓縮。在Linux或Unix系統中,TAR文件是最為常用的存檔文件(通常以.tar為文件后綴)。TAR文件的更多解釋可以參考這里。用tar命令可以生產TAR文件,示例如下:

    tar cvf tmp.tar tmp/
    

    具體參數在下面會具體解釋,不帶任何壓縮格式的話,tar命令會生成一個TAR文件。相應的,如果需要解開TAR文件,可以這么做:

    tar xvf tmp.tar
    
  • 壓縮文件(Compressed File)

    TAR文件是沒有被壓縮的,大小基本保持不變。如果需要對文件進行壓縮,則需要在tar命令中加入壓縮格式對應的參數,具體在下面會說明。

下面來詳細介紹tar命令的用法,tar的用法如下:

# tar --help
Usage: tar [OPTION...] [FILE]...

tar命令支持非常多的參數,這里列舉比較常用的幾種使用方式:

  • 壓縮文件或目錄

    # tar czvf tmp.tgz tmp/
    tmp/
    tmp/Test1
    tmp/test.log
    tmp/test2
    

    其中,czvf表示參數選項;tmp.tgz表示壓縮后的文件名,通常tgztar.gz的簡寫;tmp/表示被壓縮的目錄。參數的解釋如下:

    參數 說明
    c Create的簡寫,表示生產壓縮文件
    z 表示采用gzip的壓縮格式,文件后綴通常為.tar.gz或者.tgz
    v 表示顯示壓縮的過程,會列出所有被壓縮的文件
    f 指定壓縮文件

需要注意的是,tar支持不同的壓縮格式,除了gzip之外,還有:

參數 說明
j 采用bzip2的壓縮格式,文件后綴通常為.bz2
--lzip 采用lzip的壓縮格式,文件后綴通常為.lz
--xz 采用xz的壓縮格式,文件后綴通常為.xz
--lzma 采用lzma的壓縮格式,文件后綴通常為.lzma

更多格式可以參考這里。當然,最為常用的兩種格式為gzipbzip2,用法示例如下:

 tar czvf tmp.tgz tmp/
 tar cjvf tmp.bz2 tmp/

其中v參數可選,如果不需要顯示壓縮過程的話。需要注意的是,tar壓縮目錄的時候會保持目錄的結構。

  • 解壓文件

    對應于不同的壓縮格式,解壓參數稍微不一樣,對于gzipbzip2分別示例如下:

    tar xzvf tmp.tgz
    tar xjvf tmp.tgz
    

    與壓縮不同,c換成x表示解壓,其它參數含義與壓縮一樣。默認情況下,解壓的文件會放在當前目錄,如果需要解壓到某個目錄下,則可以用-C參數:

    tar xzvf -C /tmp/ tmp.tgz
    tar xjvf -C /tmp/ tmp.tgz
    
  • 列出壓縮包里面的文件

    有時候我們需要先看看壓縮包里面有哪些文件,但又并不想解壓文件,可以采用-t參數:

    # tar czf tmp.tgz tmp/
    # tar tvf tmp.tgz
    drwxr-xr-x root/root         0 2017-11-17 02:46 tmp/
    -rw-r--r-- root/root         0 2017-11-16 00:17 tmp/Test1
    -rw-r--r-- root/root       102 2017-11-17 00:30 tmp/test.log
    -rw-r--r-- root/root        34 2017-11-17 02:46 tmp/test2
    

    其中,tvf會列出壓縮包中的文件,不論采用何種壓縮格式,甚至是沒有被壓縮的TAR文件。

  • 從壓縮包中提取特定的文件

    在列出壓縮包里面的內容后,如果只想提取里面的某些文件,可以這么做:

    # tar xzvf tmp.tgz tmp/test.log tmp/test2
    tmp/test.log
    tmp/test2
    

    其中,根據不同的壓縮格式請替換成不同的參數。請務必注意,指定的文件必須是完整的路徑,而不能只是文件名。

    另外,如果想提取一組匹配某種條件的文件,可以使用--wildcards參數:

    # tar xzvf tmp.tgz --wildcards "*.log"
    tmp/test.log
    

split命令

split命令用來將一個文件分成多個文件,比如將一個特別大的文件分成平均大小為100M的多個文件等。

# split -b 40M go1.6.linux-amd64.tar.gz go1.6.linux-amd64.tar.gz.part
# ll go1.6.linux-amd64.tar.gz.part*
-rw-r--r--. 1 root root 41943040 Nov 20 15:03 go1.6.linux-amd64.tar.gz.partaa
-rw-r--r--. 1 root root 41943040 Nov 20 15:03 go1.6.linux-amd64.tar.gz.partab
-rw-r--r--. 1 root root   913400 Nov 20 15:03 go1.6.linux-amd64.tar.gz.partac

其中,go1.6.linux-amd64.tar.gz是要被拆分的文件,go1.6.linux-amd64.tar.gz.part是拆分后文件的前綴,可以看到文件被拆分為三部分了。

split非常常見的用法是來將某個被壓縮的文件拆分成小的部分,正如上例所示。那么,如何將拆分的文件重新合并呢?我們可以用cat將它們合并:

cat go1.6.linux-amd64.tar.gz.part* > go1.6.linux-amd64.tar.gz

grep命令

過濾數據來說,用的最多的估計就是grep命令了,grep命令可以從文件或者管道中搜索數據并打印出來,當然,其也可以直接在目錄中搜索所有的文件,并把其中符合條件的行打印出來。

# cat test.log | grep hello
hello
# grep hello test.log
hello
# grep hello . -r
./test.log:hello

上面就是三種方式搜索包括hello關鍵字的行。

grep是Linux中使用最為頻繁的命令之一,其本身也有非常強大的功能,這一節咱們將詳細講述其比較常見的用法。

基本查找

查看grep的幫助可以看到:

# grep --help
Usage: grep [OPTION]... PATTERN [FILE]...

其中OPTION指的是命令參數,PATTERN指的是匹配的字符串,比如關鍵字搜索。FILE指的是文件,當然,沒有文件的時候也可以通過管道接收數據并搜索過濾。grep提供了非常多的命令參數用來控制查找的方式跟效果,下面列舉其常用的一些參數:

參數 說明
-i 默認情況下,grep命令的搜索是大小寫敏感的,如果需要忽略大小寫可以用這個參數
-v 該參數表示不包含的意思
-A2 其中Aafter的意思,表示同時顯示搜索出來行后面兩行。有時候我們需要知道匹配行后面是什么,可以用這個參數
-B2 A相反,其是before的意思,表示同時顯示匹配行前兩行
-C2 有時候我們想既顯示前面兩行也顯示后面兩行,這時候就用這個參數
-r 搜索目錄的時候需要帶上這個參數
-n 有些時候,我們需要知道匹配到的行是第幾行,可以加上這個參數把行號打印出來

對于PATTERNgrep命令也支持不同的用法:

  • 關鍵字匹配

這個是最常用的基本用法,匹配是否包括該關鍵字的行。

  • 正則匹配

除了基本的關鍵字匹配,grep還支持極為強大的正則表達式的匹配。我們將在下一小節專門講述正則表達式匹配。

  • 或匹配

有時候我們需要匹配某個PATTERN1或者PATTERN2的行,這時候可以這么寫PATTERNPATTERN1\|PATTERN2。通過\|將多個PATTERN連在一起表示或的意思,只要匹配其中任一個的行都會被打印出來。

# grep '12306\|test' . -r
./test2:http://abcdefg.test.com
./test2:12306.com

正則表達式匹配

grep命令支持非常強大的正則匹配,支持三種不同的正則表達式:

當然,比較常用的是ERE正則表達式了。在grep命令中,采用-E參數即可以使用該表達式,正則表達式非常靈活,用法非常多,這里列舉幾個示例:

  • 匹配以關鍵字開頭的行

    grep -E "^keyword" . -r
    
  • 匹配以關鍵字結尾的行

    grep -E "keyword$" . -r
    
  • 匹配包含如2017/10/11日期的行

    grep -E "[0-9]{4}/[0-9]{2}/[0-9]{2}" . -r
    

    注意的是,對于數字的表示,其不支持如\d這樣的表達方式,而需要[0-9]這樣表達。

當然,如果需要熟練掌握grep的正則表達式匹配,你必須對正則表達式非常熟悉,這個就不在本篇的范疇了。

awk命令

要說Linux中最為強大的基本命令有哪些,那awk無疑會榜上有名,其強大的流式處理能力,很多人甚至寫書來專門講這個命令。這里咱們暫時介紹基本的功能,以后有機會將專門開辟一文來展開。

一個常見的用法就是把文件中的某幾列打印出來:

# cat test.log
Nov 17 00:27:20 traffic-base1 named[1212]: managed-keys-zone: Unable to fetch DNSKEY set .: timed out
# cat test.log | awk '{print $1, $2, $3}'
Nov 17 00:27:20

如上例,空格將一行分成了不同的列,咱們希望只把時間顯示出來,而時間包括了第1,2,3列,因此,通過awk '{print $1, $2, $3}'就實現了此功能,其中$1就是引用第一列。

cut命令

有時候,一行內的數據并不是通過空格分隔開的,而是通過其它分隔符,那如何顯示想要的列呢?通過cut命令,咱們同樣可以輕松實現:

# head -n 5 /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
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
# head -n 5 /etc/passwd | cut -d: -f1
root
bin
daemon
adm
lp

/etc/passwd中的每一行基本上都是通過:進行分隔,其中第一列表示用戶名,如果咱們只想把用戶名輸出的話,可以通過cut -d: -f1來實現,其中-d:表示以:號為分隔符,-f1表示顯示第一列。可見,對于cut命令來說,咱們可以指定分隔符,那么前面通過awk實現的例子也可以通過cut來做:

# cat test.log
Nov 17 00:27:20 traffic-base1 named[1212]: managed-keys-zone: Unable to fetch DNSKEY set .: timed out
# cat test.log | cut -d" " -f1-3
Nov 17 00:27:20

其中-d" "表示以空格為分隔符(注意,這里的空格必須以引號括起來,不然會被shell展開去除多余空格),-f1-3表示輸出第1到3列,這里簡用了-來表示范圍,當然也可以寫成-f1,2,3了。

此外,cut命令還可以指定輸出哪些位的字符:

# cat test.log
Nov 17 00:27:20 traffic-base1 named[1212]: managed-keys-zone: Unable to fetch DNSKEY set .: timed out
# cat test.log | cut -c1-16
Nov 17 00:27:20

其中,-c1-16表示輸出第1到16個字符,當然,你同樣可以以,來分別列舉要輸出哪幾個。

tr命令

tr其實是translate的縮寫,這個命令用來將某些字符翻譯成另外的字符。

# tr --help
Usage: tr [OPTION]... SET1 [SET2]

這個命令就是把字符集SET1中的字符對應的轉成字符集SET2中的字符。如將小寫轉成大寫:

# cat test.log
Nov 17 00:27:20 traffic-base1 named[1212]: managed-keys-zone: Unable to fetch DNSKEY set .: timed out
# cat test.log | tr a-z A-Z
NOV 17 00:27:20 TRAFFIC-BASE1 NAMED[1212]: MANAGED-KEYS-ZONE: UNABLE TO FETCH DNSKEY SET .: TIMED OUT

這里用-來方便的表示一定范圍的字符集,當然,你完全可以一個個列出來你要的字符集。通常情況下,SET1SET2的長度保持一致,因為這個轉換實際上是一對一的轉換,當然,SET2的長度是可以大于SET1的,多余的字符不會被使用。但是,當SET2長度小于SET1時,tr命令會將SET2中最后一個字符填充不足的位數:

# echo 'abcdefg' | tr ab wyz
wycdefg
# echo 'abcdefg' | tr abcdefg wyz
wyzzzzz

對于tr還有一個常見的用途,就是用來去除字符串中的換行符:

# head -n 5 /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
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
# head -n 5 /etc/passwd | tr '\n' ' '
root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

其中,\n表示換行符,示例中將換行符全部換成了空格。

最后,tr還有一個常見的用法,可以加上-d參數來刪除字符集中的字符。

# echo abcdeWXYZ | tr -d a-z
WXYZ

示例中刪除了所有小寫字母。

sed命令

流式處理中,sed也是一個極為常用的命令,它可以用來替換字串,比之前的tr那是要強大無數倍了。

查看sed的幫助:

# sed --help
Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]...

它既可以直接在文件中替換字符串,也可以加收管道的數據。如基本用法:

# echo abcdefgabcd | sed 's/abc/ABC/'
ABCdefgabcd

其中's/abc/ABC/'指定了替換的規則,默認情況下只替換一次,如果需要全部替換,則需要在規則后面加入g

# echo abcdefgabcd | sed 's/abc/ABC/g'
ABCdefgABCd

規則中默認使用了/為分隔。當然,你也可以使用其他分隔符,這在要替換的字串中帶有/的時候特別有用:

# echo "http://abc.test.com" | sed 's/http:\/\//https:\/\//'
https://abc.test.com
# echo "http://abc.test.com" | sed 's|http://|https://|'
https://abc.test.com

如果不指定新的分隔符|,那么就得使用轉義符\//進行轉義了,這樣可讀性就差了很多,采用|就自然多了。sed支持的分隔符還包括了:_

此外,sed不僅可以用來替換字串,還可以用來刪除匹配的行:

# cat test2
http://abcdefg.test.com
12306.com
# cat test2 | sed '/12306/d'
http://abcdefg.test.com

高級用法

  • 后向引用(Back Referencing)

    sed規則匹配到的字符串還可以在規則定義中被引用,如下例:

    # echo "hello world" | sed 's/hello/&&/'
    hellohello world
    

    其中,&可以用來引用被匹配到的字串,在本例中,匹配到的字串是hello,這樣通過&就可以引用被匹配到的字串了。此外,我們還可以通過()來指定匹配的字串,并用\1(數字表示第一個())來引用(實際上是正則表達式中的Grouping):

    # echo Sunday | sed 's/\(Sun\)/\1ny/'
    Sunnyday
    # echo Sunday | sed 's/\(Sun\)/\1ny \1/'
    Sunny Sunday
    
  • 正則匹配

    由上述可以看出,sed的規則使用了正則表達式規則,但是其書寫跟一般的正則書寫不一樣,你必須將有關的字符轉義,否則sed仍然會將其當做普通字符進行匹配:

    # echo "this is aaaa cat" | sed 's/a{4}/a/'
    this is aaaa cat
    # echo "this is aaaa cat" | sed 's/a\{4\}/a/'
    this is a cat
    

    可以看出,在沒有轉義{}之前,sed并沒有匹配到目標字串aaaa,而將其轉義之后,則以正則表達式a{4}匹配到了aaaa并進行了替換。

  • 遞歸替換

    有時候我們需要在項目目錄下替換某個字串,比如把手誤寫錯的#include<stdllib.h>全部替換成#include<stdlib.h>,希望被替換的文件包括*.cpp*.h的文件。其中的一種做法如下:

    $ head -n 1 test.cpp
    #include <stdllib.h>
    $ sed -i 's/#include <stdllib.h>/#include <stdlib.h>/' `find . -name "*.cpp" -o -name "*.h"`
    $ head -n 1 test.cpp
    #include <stdlib.h>
    

    這里用到了shell嵌入的用法(可以參看這一篇博文),通過find命令找出所有的源文件,然后用sed -i進行替換,其中-i表示從文件里面替換。這一用法會將find找到的所有文件作為參數都追加到sed命令后,在項目非常大的情況下可能會導致命令執行失敗(因為數量龐大的文件導致追加的參數太大了),通常我們推薦采用find的這種用法:

    find . -name "*.cpp" -o -name "*.h" -exec sed -i 's/#include <stdllib.h>/#include <stdlib.h>/' {} \;
    

    具體可以參照本文中關于find命令的介紹。

本博文還可以在博主個人主頁中找到。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容