首頁 > 軟體

Linux dd、split、csplit命令

2020-06-16 17:08:48

在Linux最常用的檔案生成和切片工具是dd,它功能比較全面,但無法以行為單位提取檔案資料,也無法直接將檔案按大小或行數進行均分(除非藉助迴圈)。另兩款資料分割工具split和csplit能夠比較輕鬆地實現這些需求。csplit是split的升級版。

1.1 dd命令

從if指定的檔案讀取資料,寫入到of指定的檔案。使用bs指定讀取和寫入的塊大小,使用count指定讀取和寫入的資料塊數量,bs和count相乘就是檔案總大小。可以指定skip忽略讀取if指定檔案的前多少個塊,seek指定寫入到of指定檔案時忽略前多少個塊。

dd if=/dev/zero of=/tmp/abc.1 bs=1M count=20

if是input file,of是output file;bs有c(1byte)、w(2bytes)、b(512bytes)、kB(1000bytes)、K(1024bytes)、MB(1000)、M(1024)和GB、G等幾種單位。因此,不要隨意在單位後加上字母B。

假設現有檔案CentOS.iso的大小1.3G,需要將其切分後還原,切分的第一個小檔案大小為500M。

dd if=/tmp/CentOS.iso of=/tmp/CentOS1.iso bs=2M count=250

生成第二個小檔案,由於第二個小檔案不知道具體大小,所以不指定count選項。由於第二個小檔案要從第500M處開始切分,於是需要忽略CentOS.iso的前500M。假設bs=2M,於是skip掉的資料塊數量為250。

dd if=/tmp/CentOS.iso of=/tmp/CentOS2.iso bs=2M skip=250

現在CentOS.iso=CentOS1.iso+CentOS2.iso。可以將CentOS[1-2].iso還原。

cat CentOS1.iso CentOS2.iso >CentOS_m.iso

比較CentOS_m.iso和CentOS.iso的md5值,它們是完全一樣的。

shell> md5sum CentOS_m.iso CentOS.iso
504dbef14aed9b5990461f85d9fdc667  CentOS_m.iso
504dbef14aed9b5990461f85d9fdc667  CentOS.iso

那麼seek選項呢?和skip有什麼區別?skip選項是忽略讀取時的前N個資料塊,而seek是忽略寫入檔案的前N個資料塊。假如要寫入的檔案為a.log,則seek=2時,將從a.log的第3個資料塊開始追加資料,如果a.log檔案本身大小就不足2個資料塊,則缺少的部分自動使用/dev/zero填充。

於是,在有了CentOS1.iso的基礎上,要將其還原為和CentOS.iso相同的檔案,可以使用下面的方法:

dd if=/tmp/CentOS.iso of=/tmp/CentOS1.iso bs=2M skip=250 seek=250

還原後,它們的md5值也是相同的。

shell> md5sum CentOS1.iso CentOS.iso
504dbef14aed9b5990461f85d9fdc667  CentOS1.iso
504dbef14aed9b5990461f85d9fdc667  CentOS.iso

1.2 split命令

split工具的功能是將檔案切分為多個小檔案。既然要生成多個小檔案,必然要指定切分檔案的單位,支援按行切分以及按檔案大小切分,另外還需解決小檔案命名的問題。例如,檔名字首、字尾。如果未明確指定字首,則預設的字首為"x"。

以下是命令的語法說明:

split [OPTION]... [INPUT [PREFIX]]
  
-a N:生成長度為N的字尾,預設N=2
-b N:每個小檔案的N,即按檔案大小切分檔案。支援K,M,G,T(換算單位1024)或KB,MB,GB(換算單位1000)等,預設單位為位元組
-l N:每個小檔案中有N行,即按行切分檔案
-d N:指定生成數值格式的字尾替代預設的字母字尾,數值從N開始,預設為0。例如兩位長度的字尾01/02/03
--additional-suffix=string:為每個小檔案追加額外的字尾,例如加上".log"。有些老版本不支援該選項,在CentOS 7.2上已支援。
  
INPUT:指定待切分的輸入檔案,如要切分標準輸入,則使用"-"
PREFIX:指定小檔案的字首,如果未指定,則預設為"x"

例如,將/etc/fstab按行切分,每5行切分一次,並指定小檔案的字首為"fs_",字尾為數值字尾,且字尾長度為2。

[root@linuxidc ~]# split -l 5 -d -a 2 /etc/fstab fs_

[root@linuxidc ~]# ls
fs_00  fs_01  fs_02

檢視任一小檔案。

[root@linuxidc ~]# cat fs_01
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
UUID=b2a70faf-aea4-4d8e-8be8-c7109ac9c8b8 /                       xfs     defaults        0 0
UUID=367d6a77-033b-4037-bbcb-416705ead095 /boot                   xfs     defaults        0 0

可以將這些切分後的小檔案重新組裝還原。例如,將上面的三個小檔案還原為~/fstab.bak。

[root@linuxidc ~]# cat fs_0[0-2] >~/fstab.bak

還原後,它們的內容是完全一致的。可以使用md5sum比較。

[root@linuxidc ~]# md5sum /etc/fstab ~/fstab.bak
29b94c500f484040a675cb4ef81c87bf  /etc/fstab
29b94c500f484040a675cb4ef81c87bf  /root/fstab.bak

還可以將標準輸入的資料進行切分,並分別寫入到小檔案中。例如:

[root@linuxidc ~]# seq 1 2 15 | split -l 3 -d - new_

[root@linuxidc ~]# ls new*
new_00  new_01  new_02

可以為每個小檔案追加額外的字尾。有些老版本的split不支援該選項,而是在csplit上支援的,但是新版本的split已經支援。例如,加上".log"。

[root@linuxidc ~]# seq 1 2 20 | split -l 3 -d -a 3 --additional-suffix=".log" - new1_

[root@linuxidc ~]# ls new1*
new1_000.log  new1_001.log  new1_002.log  new1_003.log

 

1.3 csplit命令

split只能按行或按照大小進行切分,無法按段落切分。csplit是split的變體,功能更多,它主要是按指定上下文按段落分割檔案。

csplit [OPTION]... FILE PATTERN...
 
描述:按照PATTERN將檔案切分為"xx00","xx01", ...,並在標準輸出中輸出每個小檔案的位元組數。
 
選項說明:
-b FORMAT:指定檔案字尾格式,格式為printf的格式,預設為%02d。表示字尾以2位數值,且不足處以0填充。
-f PREFIX:指定字首,不指定是預設為"xx"-k:用於突發情況。表示即使發生了錯誤,也不刪除已經分割完成的小檔案。
-m:明確禁止檔案的行去匹配PATTERN。
-s:(silent)不列印小檔案的檔案大小。
-z:如果切分後的小檔案中有空檔案,則刪除它們。
 
FILE:待切分的檔案,如果要切分標準輸入資料,則使用"-"。
 
PATTERNs:
 
INTEGER         :數值,假如為N,表示拷貝1到N-1行的內容到一個小檔案中,其餘內容到另一個小檔案中。
/REGEXP/[OFFSET]:從匹配到的行開始按照偏移量拷貝指定行數的內容到小檔案中。
                :其中OFFSET的格式為"+N""-N",表示向後和向前拷貝N行
%REGEXP%[OFFSET]:匹配到的行被忽略。
{INTEGER}       :假如值為N,表示重複N此前一個模式匹配。
{*}             :表示一直匹配到檔案結尾才停止匹配。

假設檔案內容如下:

[root@linuxidc ~]# cat test.txt
SERVER-1
[connection] 192.168.0.1 success
[connection] 192.168.0.2 failed
[disconnect] 192.168.0.3 pending
[connection] 192.168.0.4 success
SERVER-2
[connection] 192.168.0.1 failed
[connection] 192.168.0.2 failed
[disconnect] 192.168.0.3 success
[CONNECTION] 192.168.0.4 pending
SERVER-3
[connection] 192.168.0.1 pending
[connection] 192.168.0.2 pending
[disconnect] 192.168.0.3 pending
[connection] 192.168.0.4 failed

假設每個SERVER-n表示一個段落,於是要按照段落切分該檔案,使用以下語句:

[root@linuxidc ~]# csplit -f test_  -b %04d.log test.txt /SERVER/ {*}
0
140
139
140

 "-f test_" 指定小檔案字首為"test_", "-b %04d.log" 指定檔案字尾格式"00xx.log",它自動為每個小檔案追加額外的字尾".log", "/SERVER/" 表示匹配的模式,每匹配到一次,就生成一個小檔案,且匹配到的行是該小檔案中的內容, "{*}" 表示無限匹配前一個模式即/SERVER/直到檔案結尾,假如不知道{*}或指定為{1},將匹配一次成功後就不再匹配。

[root@linuxidc ~]# ls test_*
test_0000.log  test_0001.log  test_0002.log  test_0003.log

上面的檔案中雖然只有三個段落:SERVER-1,SERVER-2,SERVER-3,但切分的結果生成了4個小檔案,並且注意到第一個小檔案大小為0位元組。為什麼會如此?因為在模式匹配的時候,每匹配到一行,這一行就作為下一個小檔案的起始行。由於此檔案第一行"SERVER-1"就被/SERVER/匹配到了,因此這一行是作為下一個小檔案的內容,在此小檔案之前還自動生成一個空檔案。

生成的空檔案可以使用"-z"選項來刪除。

[root@linuxidc ~]# csplit -f test1_ -z -b %04d.log test.txt /SERVER/ {*}
140
139
140

還可以指定只拷貝匹配到的行偏移數量。例如,匹配到行時,只拷貝它後面的1行(包括它自身共兩行),但多餘的行將放入下一個小檔案中。

[root@linuxidc ~]# csplit -f test2_ -z -b %04d.log test.txt /SERVER/+2 {*}
42
139
140
98

第一個小檔案只有兩行。

[root@linuxidc ~]# cat test2_0000.log 
SERVER-1
[connection] 192.168.0.1 success

SERVER-1段落的其餘內容放入到了第二個小檔案中。

[root@linuxidc ~]# cat test2_0001.log
[connection] 192.168.0.2 failed
[disconnect] 192.168.0.3 pending
[connection] 192.168.0.4 success
SERVER-2
[connection] 192.168.0.1 failed

同理第三個小檔案也一樣,直到最後一個小檔案中存放剩餘所有無法匹配的內容。

[root@linuxidc ~]# cat test2_0003.log 
[connection] 192.168.0.2 pending
[disconnect] 192.168.0.3 pending
[connection] 192.168.0.4 failed

指定"-s"或"-q"選項以靜默模式執行,將不會輸出小檔案的大小資訊。

[root@linuxidc ~]# csplit -q -f test3_ -z -b %04d.log test.txt /SERVER/+2 {*}

本文永久更新連結地址http://www.linuxidc.com/Linux/2017-08/146610.htm


IT145.com E-mail:sddin#qq.com