dd、split、csplit命令

在Linux最經常使用的文件生成和切片工具是dd,它功能比較全面,但沒法以行爲單位提取文件數據,也沒法直接將文件按大小或行數進行均分(除非藉助循環)。另兩款數據分割工具split和csplit可以比較輕鬆地實現這些需求。csplit是split的升級版。redis

在處理很大的文件時,一個很是高效的思路是將大文件切割成多個小文件片斷,而後再經過多個進程/線程對各個小文件進行操做,最後合併總數居。就像sort命令,它在實現排序時,底層算法就涉及到了將一個大文件切割成多個臨時小文件。算法

1.1 dd命令

從if指定的文件讀取數據,寫入到of指定的文件。使用bs指定讀取和寫入的塊大小,使用count指定讀取和寫入的數據塊數量,bs和count相乘就是文件總大小。能夠指定skip忽略讀取if指定文件的前多少個塊,seek指定寫入到of指定文件時忽略前多少個塊。shell

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。spa

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還原。code

cat CentOS1.iso CentOS2.iso >CentOS_m.iso

比較CentOS_m.iso和CentOS.iso的md5值,它們是徹底同樣的。blog

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上已支持。
-n CHUNKS:將文件按照指定的CHUNKS方式進行分割。CHUNKS的有效形式有(具體用法見後文):N、l/N、l/K/N、K/N、r/N、r/K/N
--filter CMD:再也不直接切割輸出到文件,而是切割後做爲管道的輸入,經過管道將切割的數據傳遞給CMD執行。若是須要指定文件,則split自動使用$FILE變量。見後文示例

INPUT:指定待切分的輸入文件,如要切分標準輸入,則使用"-"
PREFIX:指定小文件的前綴,若是未指定,則默認爲"x"

1.2.1 基本用法

例如,將/etc/fstab按行切分,每5行切分一次,並指定小文件的前綴爲"fs_",後綴爲數值後綴,且後綴長度爲2。

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

[root@xuexi ~]# ls
fs_00  fs_01  fs_02

查看任一小文件。

[root@xuexi ~]# 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@xuexi ~]# cat fs_0[0-2] >~/fstab.bak

還原後,它們的內容是徹底一致的。可使用md5sum比較。

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

還能夠將標準輸入的數據進行切分,並分別寫入到小文件中。例如:

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

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

能夠爲每一個小文件追加額外的後綴。有些老版本的split不支持該選項,而是在csplit上支持的,可是新版本的split已經支持。例如,加上".log"。

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

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

1.2.2 按CHUNKS進行分割

split的"-n"選項是按照CHUNK的方式進行文件切割:

'-e' 當使用-n時,不要生成空文件。例如5行數據,卻要求按行切割成100個文件,顯然第5個文件以後都是空文件

'-u --unbuffered' 當使用-n的r模式時,不要緩衝input,每讀一行就當即拷貝給輸出,因此該選項可能會比較慢

'-n CHUNKS'
'--number=CHUNKS'
     Split INPUT to CHUNKS output files where CHUNKS may be:
          N      根據文件大小均分爲N個文件(最後一個文件可能大小不均)
          K/N    輸出(打印到屏幕、標準輸出)N個文件中的第K個文件內容(不是切割後再找到這個文件進行輸出,而是切割到這個文件時當即輸出)
          l/N    按行的方式均分爲N個文件(最後一個文件可能行數不均)
          l/K/N  按l/N切割的同時,輸出屬於第K個文件的內容
          r/N    相似於l的形式,但對行進行輪詢方式切割。例如第一行到第一個文件,第二行到第二個文件
          r/K/N  按照r/N切割時,輸出第K個文件的內容
 
其中大寫字母K和N是咱們按需指定的數值,l或r是表明模式的字母

可能不是很好理解,給幾個示例就很清晰了。

假如文件1.txt中有a-z,每一個字母佔一行,共26行。將這個文件進行切割:

1.指定CHUNK=N或K/N時

將1.txt均分爲5個文件。因爲1.txt共52字節(每行中,字母一個字節,換行符一個字節),因此分割後每一個文件10字節,最後一個文件12字節。

[root@redisa-b a]# split -n 5 1.txt fs_

[root@redisa-b a]# ls -l
total 24
-rw-r--r-- 1 root root 52 Oct  6 15:23 1.txt
-rw-r--r-- 1 root root 10 Oct  6 16:07 fs_aa
-rw-r--r-- 1 root root 10 Oct  6 16:07 fs_ab
-rw-r--r-- 1 root root 10 Oct  6 16:07 fs_ac
-rw-r--r-- 1 root root 10 Oct  6 16:07 fs_ad
-rw-r--r-- 1 root root 12 Oct  6 16:07 fs_ae

若是再指定K/N的K,例如指定爲2,則輸出屬於fs_ab文件中的內容:

[root@redisa-b a]# split -n 2/5 1.txt fs_
f
g
h
i
j

2.指定CHUNK=l/N或l/K/N時

這時將按照行的總數量進行均分。例如,將1.txt中的26行分割成5個文件,前4個文件將各有5行,第5個文件將有6行:

[root@redisa-b a]# split -n l/5 1.txt fs_

[root@redisa-b a]# wc -l fs_*
 5 fs_aa
 5 fs_ab
 5 fs_ac
 5 fs_ad
 6 fs_ae
26 total

若是指定K,則輸出屬於第K個文件的內容:

[root@redisa-b a]# split -n l/2/5 1.txt fs_
f
g
h
i
j

3.指定CHUNK=r/N或r/K/N時

使用r時,將每切割一行就輪詢到下一個文件,輪完全部文件再返回來切割到第一個文件。

直接看結果:

[root@redisa-b a]# split -n r/5 1.txt fs_  

[root@redisa-b a]# head -n 2 fs*
==> fs_aa <==
a
f
 
==> fs_ab <==
b
g
 
==> fs_ac <==
c
h
 
==> fs_ad <==
d
i
 
==> fs_ae <==
e
j

a輸出到第1個文件,b輸出到第2個文件,c輸出到第3個文件,依此類推。

指定K時,將輸出第K個文件的內容:

[root@redisa-b a]# split -n r/2/5 1.txt fs_
b
g
l
q
v

1.2.3 filter CMD將切割結果傳遞給指定命令

默認狀況下split是將文件切割傳遞到各文件片斷中,若是使用--filter選項,則再也不切割到文件片斷中,而是經過管道的形式傳遞給CMD進行處理。CMD處理時,有可能並不須要將數據存儲到文件中,但若是須要將處理後的數據再分片斷保存到多個文件中,則可使用$FILE來表示(這是split識別的變量,要避免被shell解析),就像split的普通切割模式同樣。

例如,讀取1.txt中的26行,每5行管道傳遞一次,而後使用echo將它們輸出:

[root@redisa-b a]# split -n l/5 --filter='xargs -i echo ---:{}' 1.txt
---:a
---:b
---:c
---:d
---:e   # 第1次經過管道傳遞的內容
---:f
---:g
---:h
---:i
---:j   # 第2次經過管道傳遞的內容
---:k
---:l
---:m
---:n
---:o
---:p
---:q
---:r
---:s
---:t
---:u
---:v
---:w
---:x
---:y
---:z

這時split是沒有生成任何小文件片斷的。若是想要將上面的輸出保存到小文件片斷中,使用$FILE,這個變量是split內置變量,不能被shell解析,因此出現$split的地方必須使用單引號保護起來:

[root@redisa-b a]# split -n l/5 --filter='xargs -i echo ---:{} >$FILE.log' 1.txt fs_

[root@redisa-b a]# ls
1.txt  fs_aa.log  fs_ab.log  fs_ac.log  fs_ad.log  fs_ae.log
[root@redisa-b a]# cat fs_aa.log
---:a
---:b
---:c
---:d
---:e

其中的$FILE就是split進行命名的部分。上面的小文件中,都是以fs_爲前綴,以".log"爲後綴。

filter有時候是頗有用的,例如將一個大的壓縮文件,切割成多個小的壓縮文件。

xz -dc BIG.xz | split -b200G --filter='xz > $FILE.xz' - big-

"xz -dc"表示解壓到標準輸出,解壓的數據流將傳遞給split,而後每200G就經過filter中的xz命令進行壓縮,壓縮後的文件名格式相似於"big-aa.xz big-ab.xz"。

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@xuexi ~]# 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@xuexi ~]# 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@xuexi ~]# 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@xuexi ~]# csplit -f test1_ -z -b %04d.log test.txt /SERVER/ {*}
140
139
140

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

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

第一個小文件只有兩行。

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

SERVER-1段落的其他內容放入到了第二個小文件中。

[root@xuexi ~]# 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@xuexi ~]# 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@xuexi ~]# csplit -q -f test3_ -z -b %04d.log test.txt /SERVER/+2 {*}
相關文章
相關標籤/搜索