ZZ 超級工具:為資料做外科手術(dd, head, tail…)

來源:互聯網
上載者:User
  

http://www.linux-mag.com/2004-10/toc.html

Linux Magazine October 2004

Copyright Linux Magazine 2004

POWER TOOLS
Performing Data Surgery
by Jerry Peek

轉載並翻譯自 linux magazine 網站,譯文遵循 GNU FDL,僅本文部分可自由修改,重發布時本文之外的部分必須同時原樣發布。譯者對未告知作者表示歉意,但對誤讀的後果表示不負責。

超級工具: 為資料做外科手術
(作者 Jerry Peek)

本文開始

一年以前,2003 年 11 月的“超級工具”欄目 (可以線上查看,網址是http://www.linux-mag.com/2003-11/power_01.html ) 介紹了一些少有人知的文本編輯工具:行編輯器 ex 和 ed,以及流編輯器 sed

這個月,我們更深入一點,看看幾乎無人知曉的工具 dd 的一些妙用。dd 的作用不僅是從磁帶機讀取資料,儘管那是它多年以來主要的用途。同時,我們會涉及到一些常用的東東,包括編輯工具 head 和 tail,八進位轉儲工具 od,以及裝置 /dev/random 等等。

準備好了嗎?

資料僅僅是資料而已

我們先看看 linux 是如何對付資料的。

通常,不管資料來自何處——磁碟,網路,磁帶……——它們都僅僅是位元組的序列而已。這意味著你不必關心如何從磁碟中讀或者如何向磁帶中寫。你只要讀寫位元組 的序列就可以了,而裝置驅動負責轉換為裝置的內部格式——磁碟區塊,網路資料包等等。(但是,有時知道裝置的塊大小會很有好處,接下來我們也會遇到。)

只有一個資料格式問題是所有人都必須知道的:按行組織的資料和位元據的區別。

在按行組織的資料中,一個“行”是一個字元組成的序列,以新行符結束。(很多程式語言中以兩個字元 "/n" 來表示某處是一個新行符,實際上在資料流中,它只佔用一個字元的位置。)

例如,當 head 輸出一個檔案的頭 10 行時,它從檔案中讀取一系列的位元組,直到遇到十個新行符為止。它可能會調用十次某個庫函數來做這件事,這個庫函數知道如何從一個位元組流中讀取一行。下面是一個可以用來做實驗的文字檔引用:

What's In That File?

The od ("octal dump") utility displays a file in various formats. It's great for looking at a non-text file without messing up your terminal, or for seeing exactly what's in a text file.

For instance, let's use the Bourne shell's quoting operators to store three lines of text in a file named textfile. Then we'll dump it with od -c, which shows data in a character format:

$ echo "test
> 1
> 2" > textfile
>
$ od -c textfile
0000000 t e s t /n 1 /n 2 /n
0000011

Notice the three newlines in the file? A text file from a DOS-type system would have pairs of /r /n at the end of each line. The ending number 0000011 shows, in octal, how many bytes od displayed. (11 octal is 9 decimal, so we saw 9 bytes.)

Next let's look at the 50 bytes we read from /dev/urandom. We'll use an octal byte format (the first byte is 31 octal, the second is 62 octal, and so on):

$ od -b myrand
0000000 031 062 132 063 153 015 075 364 061 070
375 013 365 372 316 270
0000020 256 307 144 345 016 121 162 074 260 151
022 361 116 257 324 251
0000040 263 056 233 123 171 277 274 121 102 221
305 111 332 330 327 213
0000060 053 233
0000062

Using od -c instead shows that some of those random bytes are legal as characters, and others are not. This technique is useful for seeing what's in a file that doesn't seem to "look right" on your terminal.

位元據就是那些不按行組織的所有其他資料。它僅僅是一個位元組序列。它也許包含內部結構,例如,很多檔案利用檔案開始處的特殊字元序列來標識自身,但是 linux 沒有強制的檔案類型,來告訴你資料究竟是如何組織的。

如上所說,資料可以來自網路連接,管道,鍵盤等等。如果資料在一個磁碟檔案中,那麼可以為它命名,在名稱最後加上一個 . 和一個副檔名,來指示它是什麼檔案。例如,.txt 是文字檔,.jpg 是JPEG影像檔等等。一些應用程式要求命名服從這種約定。但是,大致上,linux 不關心這些。它只處理位元組的序列。

資料……我只要一點點

如果使用 cat 這樣的工具來查看檔案,或者使用 shell 的重新導向操作符 (|, < 還有 >) 來將來自某個裝置或者應用程式的資料轉送給另一個,你將一次得到所有資料,除非加以限制。

如果你只想看一個或多個文字檔的頭幾行,例如想知道檔案裡存了什麼,可以用 head 工具。預設情況下,head 顯示檔案的頭十行。如果要處理多個檔案,就在每個檔案前輸出一個說明。可以調整它的行為,例如輸出特定位元組數而不是行數,使用命令列選項來控制。head 的文法在各個系統上可能不一樣,運行 man head 來查看細節。tail 工具與此類似,但顯示檔案的最後幾行,大多數版本的 tail 也可以處理多個檔案。

類似其他設計優美的 linux 工具,head 和 tail 都可以從標準輸入來讀取資訊,如果不指定檔案名稱的話。這樣允許你組合使用他們,像下面這個例子中一樣

代碼: # prog | tee prog.out | tail
...last ten lines of prog output...

prog 的輸出被 tee 接受,tee 將輸入資料寫到檔案 prog.out 中,同時又將資料通過管道轉送給 tail,接下來當 prog 結束後,tail 輸出資料的最後十行。

head 和 tail 處理文字檔時工作得很好,但是處理任意檔案就無能為力了。如果 head 和 tail 在終端上輸出非文本的資料,你可能會看到一堆垃圾,還可能破壞顯示。要避免這個問題,你可以將輸出導向一個分頁器,例如 less,它可以安全地顯示不可列印字元。也可以將輸出導向到 cat 工具,要加上 -v 選項。它們將除了換行和跳格之外的控制字元轉換為雙字元,類似 ^X,將其他非 ASCII 字元轉換為 M- 加上它們的編碼的低七位。

截斷資料,第一部分

讓我們先看看如何將長的資料流寫為一系列短一些的檔案。資料可能來自例如管道或者檔案。

一種辦法是使用 split。這個簡單的工具讀取資料,並寫入為多個固定大小的檔案。它寫滿一個檔案再寫另一個,直到所有輸入都讀寫完畢。它很好用,例如當你要通過不可靠 的網路傳輸一個大檔案時,與現代的 ftp 不同,傳輸在中斷後無法恢複,因此任何傳輸問題都意味著從頭重傳整個檔案。這種情況下,最好將大檔案分割成小檔案,依次傳輸,只重傳失敗的那些,然後將小 檔案重新組裝為完整的檔案。

下面是一個例子。你要在辦公室將 100MB 的檔案 bigfile.tar.gz 分割成 100 個 1MB 的檔案,然後在家裡用撥號網路下載。可以用這個命令

代碼: office$ split -b 1m bigfile.tar.gz

現在你有了 100 個 1MB 的檔案,名字預設是 xaa, xab, xac, ... xdu 和 xdv。然後你在家裡下載了它們。要將他們重新組裝,使用 shell 的萬用字元來匹配所有三字元的檔案名稱,按照字母表的順序:

代碼: home% cat x[a-d]? > bigfile.tar.gz

最好用 md5sum 或者 sum 這樣的工具來校正一下,重新組裝的檔案 bigfile.tar.gz 與原始檔案是一樣的。

使用 dd 進行資料轉儲

dd 進行低級資料傳送,按位元組來,或者按塊來,塊的大小可以調節。它可以跳過輸入或輸出檔案的指定個數的塊,也可以轉換資料格式。這些功能在與磁帶或磁碟打交道時都非常有用,但是在很多資料轉送時也很有用。

預設情況下,dd 從標準輸入讀取,寫到標準輸出。輸入和輸出檔案名,以及其他選項的文法異於平常,必須用不帶前置連字號 "-" 的方式給出。

例如,要讀取磁碟片,將鏡像寫入一個檔案,可以這樣

代碼: $ dd if=/dev/fd0 of=dosboot.img
2880+0 records in
2880+0 records out
$ ls -l dosboot.img
-rw-rw-r- ... 1474560 Nov 2 12:59 dosboot.img

dd 命令列的意思是,從輸入檔案 /dev/fd0 讀,將所有資料寫入輸出檔案 dosboot.img。dd 不會尋找資料或磁碟檔案中的行,它進行二進位的複製,從第一個位元組直到最後一個。dd 總是在標準錯誤告訴你,它讀寫了多少次。上面的例子中,它讀了 2880 次 512 位元組的塊。如果不想看這些資訊,或者任何錯誤資訊,可以將 dd 的輸出重新導向到 Linux 的垃圾堆 /dev/null 中,只要在命令列最後添加 Bourne shell 的重新導向操作符 2>/dev/null 就可以了。

指定較大的塊會更有效率,裝置驅動讀寫次數會更少。還有其他的選項,很多選項以 "conv=" 開始,例如 "conv=unblock" 可以將塊中的尾部空格轉換為一個新行符,"conv=swap" 將交換每對輸入位元組,在將某些磁帶的資料寫到其他硬體時需要這樣做。但是我們在這裡不講這些,你可以自己去看手冊頁。我們來看一些不那麼明顯的用法。

愚蠢的把戲

需要一個包含 100 個任意位元組的檔案——例如,用於測試?Linux 裝置檔案 /dev/urandom (自 linux 1.3.30 起可用) 可以提供你需要的任意多的偽隨機位元組。要擷取僅僅 100 位元組,只要設定塊大小為 1 位元組,使用 "bs=1",然後讓 dd 在讀取 100 “塊” 後停止:(讀取了 100 位元組)

代碼: dd if=/dev/urandom of=myrand bs=1 count=100

myrand 檔案裡面有什嗎?od 工具可以告訴你。(參見上面的 《What's in That File?》那篇文章)

如果你需要更多隨機資料,就使用 /dev/random。但是從 /dev/random 讀取要花好一陣子,手冊頁 random(4) 解釋了為什麼。當從 /dev/random 讀取時,將塊大小設為 1。

dd 的另一種用法,是在刪除一個文字檔之前,將它的內容摧毀。簡單地刪除一個 linux 檔案僅僅會刪除指向資料的 inode 節點。我們可以在刪除前,用 dd 來向檔案中寫入隨機資料。通常 dd 在寫入前會截斷這個檔案,因此我們必須用 "conv=notrunc" 來指明它覆蓋已有的資料。設定 bs 為檔案大小,count 為 1,例如:

代碼: % ls -l afile
-rw------- ... 3769 Nov  2 13:41 afile
% dd if=/dev/urandom of=afile /
  bs=3769 count=1 conv=notrunc
1+0 records in
1+0 records out
% rm afile

如果你想要的話,可以將摧毀內容的過程重複多次,使用 C shell 的 repeat 命令,Z shell 的 repeat 迴圈,或者直接用曆史操作符 !!

截斷資料,第二部分

如果需要依次發送一批資料,每次都要暫停並進行一些操作,split 不能滿足需要。它只將資料寫入檔案,在所有檔案都被寫入之前不會停止。我們用 dd 來完成任務,但是需要一些 linux 如何處理資料流的知識。

2004 年 5 月 的專欄文章 《Great Command-line Combinations》(可以線上查看,網址是 http://www.linux-mag.com/2004-05/power_01.html ) 描述了一個 shell 迴圈,從流中讀取資料,一行一行地讀,直到所有資料都被讀出。在那個例子中,我們使用 read 命令來讀取 find 命令的輸出的每一行。現在我們用類似的技術來讀取 someprog 的輸出,每次讀取固定大小的塊。基本的迴圈如下

代碼: someprog |
while :
do
  chunk=$(dd count=1 bs=10 2>/dev/null)
  test -z "$chunk" && break
...process $chunk...
done

shell 操作符 ":" (冒號) 的功能是返回 0 (true,真值) 。這樣 while 迴圈將一直運行,直到迴圈體中執行了 break 命令。

bash 命令替換 "$(命令)" 從 dd 的標準輸出中截獲 10 位元組,然後儲存到 shell 變數 trunk 中。我們丟棄了 dd 的標準錯誤,包括那些真正的錯誤。

接下來的代碼測試 $chunk 是否為空白,如果是的話就跳出迴圈。它不能像 2004 年 5 月那篇文章中那樣,測試 dd 的退出狀態,因為無論是否讀取資料,dd 都返回 0。這裡使用的技巧在很多情況下都有用。記住設定 count=1,使 dd 不要在一次迴圈中讀取所有的資料。

本文結束

Jerry Peek is a freelance writer and instructor who has used Unix and Linux for over 20 years. He's happy to hear from readers; see http://www.jpeek.com/contact.html.

Linux Magazine October 2004

Copyright Linux Magazine 2004

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.