Sort

sort 命令隨意添加/刪除數據

  • May 24, 2020

我正在使用命令對我從該站點sort下載的 rockyou.txt 單詞表進行排序:

% sort rockyou.txt > rockyou_sorted.txt

但是,當我檢查兩個文件的文件大小時,它們不同,排序 後的文件更小

% du -shk rockyou_sorted.txt rockyou.txt 
147520  rockyou_sorted.txt
148304  rockyou.txt

有趣的是,當我使用從這裡下載的干淨版本的 rockyou.txt 單詞列表重複這些相同的步驟時,我得到相反的結果,即排序後的文件更大

% sort rockyou_cleaned.txt > rockyou_cleaned_sorted.txt
% du -shk rockyou_cleaned_sorted.txt rockyou_cleaned.txt 
114752  rockyou_cleaned_sorted.txt
102104  rockyou_cleaned.txt

我想知道這是為什麼?有人可以為我解釋一下嗎?難道我做錯了什麼?我認為這兩個文件,排序的文件和原始文件的大小應該相同,不是嗎?

更新 1,根據 Francesco Lucianò 在下面的評論:將此sort命令與 -o 參數一起使用

% sort rockyou.txt -o rockyou_sorted_sO.txt
% du -shk rockyou_sorted_sO.txt rockyou.txt
147996 /Users/Martin/Downloads/rockyou_sorted_sO.txt
148304 /Users/Martin/Downloads/rockyou.txt

排序後的文件仍然比原始文件小,但不如我使用sort上面的命令版本時那麼多。

所有文件中的行數都相同:

% wc -l rockyou_sorted_sO.txt rockyou_sorted.txt rockyou.txt
14344391 rockyou_sorted_sO.txt
14344391 rockyou_sorted.txt
14344391 rockyou.txt
43033173 total 

更新 2,根據下面 bey0nd 的評論: set | grep LANG什麼都不輸出:

% set | grep LANG
%

% chardet rockyou* 
zsh: command not found: chardet
% uchardet rockyou*
rockyou.txt: UTF-8
rockyou_sorted.txt: UTF-8
rockyou_sorted_duplicut.txt: UTF-8
rockyou_sorted_sO.txt: UTF-8

更新 3,根據以下 steeldriver 的評論:

% system_profiler SPSoftwareDataType
Software:

   System Software Overview:

     System Version: macOS 10.15.4 (19E287)
     Kernel Version: Darwin 19.4.0
     Boot Volume: Macintosh HD
     Boot Mode: Normal
     Computer Name: *REDACTED* MacBook Pro
     User Name: *REDACTED*
     Secure Virtual Memory: Enabled
     System Integrity Protection: Enabled
     Time since boot: 6 days 4:57

文件系統是 APFS。

更新 4,根據以下 roaima 的評論:

% ls -l rockyou*
-rw-r--r--@ 1 **REDACTED**  staff  139921497 May 16 12:24 rockyou.txt
-rw-r--r--  1 **REDACTED**  staff  139921847 May 16 12:25 rockyou_sorted.txt
-rw-r--r--  1 **REDACTED**  staff  139919642 May 16 12:29 rockyou_sorted_duplicut.txt
-rw-r--r--  1 **REDACTED**  staff  139921847 May 16 13:19 rockyou_sorted_sO.txt

% stat -f .
.

更新 5,根據艾薩克的評論如下:

% head -n3 rockyou.txt | od -An -tcx1 
          1   2   3   4   5   6  \n   1   2   3   4   5  \n   1   2   3
          31  32  33  34  35  36  0a  31  32  33  34  35  0a  31  32  33
          4   5   6   7   8   9  \n                                    
          34  35  36  37  38  39  0a 

% LC_ALL=C sort rockyou.txt >rockyou_sorted_with_LC.txt
% du -shk rockyou_sorted_with_LC.txt rockyou.txt
147520  rockyou_sorted_with_LC.txt
140476  rockyou.txt
% wc -l rockyou_sorted_with_LC.txt rockyou.txt
14344391 rockyou_sorted_with_LC.txt
14344391 rockyou.txt
28688782 total

更新 6,根據以下 fra-san 的評論:

% sort --version
2.3-Apple (101.40.1)
% locale        
LANG=""
LC_COLLATE="en_US.UTF-8"
LC_CTYPE="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_ALL="en_US.UTF-8"

這裡發生了件事,它們在某種程度上相互矛盾。

  1. 輸入文件的編碼不一致,並被轉換sort為有效的 UTF-8。這使得文件更大。這會影響ls -l-reported 的大小。
  2. 文件系統儲存發生了一些奇怪的事情,它會根據相同大小的文件的創建方式來改變使用的塊數。這會影響du -shk-reported 的大小,並且通常會使其更小(但可以採用任何一種方式)。

我可以對第一點給出比第二點更準確的解釋,儘管簡短的回答是這du不是測量單個文件大小的正確工具,尤其是在 APFS 上。

接下來的兩節深入探討了這兩個因素。


對於因素 (1),文件包含非 UTF-8 編碼的行,這是 macOS 上的預設語言環境編碼。sort命令在輸出這些錯誤編碼的行時會對其進行修改,從而使文件變大。我們將在下面進一步調查,但這是簡短的答案,如果足夠的話,您可以跳到下一部分。

如果我們在 C 語言環境中對提供的文件進行排序,然後在 en_US.UTF-8 語言環境中再次排序,這兩個文件具有不同的實際大小:

139921497 rockyou.txt
139921497 rockyou_c.txt
139921847 rockyou_sorted.txt

C 和 UTF-8 排序文件之間的第一個區別是

299c299
<  �R3CKL3$$�
---
>  R3CKL3$$

第一行在密碼的開頭和結尾包含字節 0x93 和 0x94,它們在 UTF-8 中不是有效的獨立字節(它們只能作為多字節字元的連續字節出現)。第二個包含 Unicode 程式碼點 U+0093 和 U+0094,分別編碼為兩個字節的 UTF-8C2 93C2 94.

結果是原來的 10 字節行被寫成了 12 字節行。在整個文件中,這些更改在排序後的文件中比原始文件增加了 350 個字節。

我相信這裡發生的是:

  1. 原始行是“R3CKL3$$”(包括引號),在Windows-1252 程式碼頁(cp1252) 中編碼。在該編碼中,成對的引號是 0x93 和 0x94。
  2. 它作為原始字節添加到此集合中,與許多其他條目一樣,沒有任何編碼考慮。
  3. 在真正的 Latin-1 ISO 8859-1 中,字節範圍 0x80-0x9f 未被佔用,但在 ISO-8859-1(注意額外的連字元)中,它由C1 控製字元填充。
  4. 所有 ISO-8859-1 都被編碼為Unicode 中的“ Latin-1 Supplement ”塊,程式碼點對應於原始字節值,並由 UTF-8 中的字節C2 80編碼表示。C2 BF``C3 80``C3 BF
  5. macOS 排序將單獨的連續字節解釋為嘗試的 ISO-8859-1,並在內部以這種方式將它們轉換為 Unicode 字元串以進行排序:字節 0x93 在已排序的字元串中變為 U+0093。
  6. 這些翻譯後的字元串以 UTF-8 格式輸出,而不是文件中的原始行。

該站點上的其他問題討論瞭如何在事後修復錯誤編碼的cp1252 文件,如果這是您需要的。

POSIX 指出,如果行包含在區域設置中不形成有效字元的字節序列,則該實用程序的行為是未定義的,因此**這是標準嚴格允許的,**並且不是一致性錯誤。它仍然至少是出乎意料的,可以說是一個行為錯誤。我嘗試過的其他排序實現不會以這種方式執行。

當您對文件進行排序時,這個因素會推動文件變得稍大*,*並且確實更大 - 如果您從文件中讀取,您將獲得更多字節。


因素 (2) 總體推動文件變得“更小”,但這在某種程度上是一種錯覺。讀取文件不一定會產生更多或更少的字節,因為du它們的大小不同。

du -shk一般來說,不是檢查文件大小的合適方法,因為

du 實用程序顯示每個文件參數的文件系統塊使用情況

這意味著它報告有關文件佔用了多少物理空間的資訊,而不是其邏輯大小。根據文件系統和相關文件的確切參數,塊數可能與您預期的相差很大。在某些情況下,塊計數很有用,例如當您將文件壓縮到完整的設備上時,但通常不是這樣。

塊計數在今天變得更沒用的一個原因是現代文件系統並不總是完全按照給定的方式寫入數據:例如,它們可能會在將數據儲存到更大或更小的範圍之前靜默壓縮它,需要更少的塊,或者在塊內留下空白空間使用更多塊使將來的插入更容易。稀疏文件會省略零塊,但重複數據刪除可以走得更遠。

在 APFS 的案例中,它支持使用多種算法進行壓縮、一些重複數據刪除和增量編碼、加密和高級元數據。其中一些或全部可能在起作用,最有可能在寫入文件時發生透明壓縮的變化,具體取決於應用程序實現和系統負載。

如果我們只是cat文件幾次,我們已經可以看到差異。如果我已經下載rockyou.txtcurl -O

  • cat rockyou.txt > rockyou2.txt為我創建一個字節數相同(139921497)但塊數不同的文件(-created 為curl147504,cat’s 為 147460)
  • Catting 新文件給了我另一個塊計數(147520),和cp(150512)一樣。
  • 再次執行兩條線給我的結果與第一次嘗試不同。

我不知道為什麼會這樣,我不確定是否有合理的方法來判斷。我懷疑它有時比其他人更努力地壓縮數據。在所有情況下,文件的大小實際上是相同的,從任何版本讀取都會返回相同的字節。我們只是沒有從報告的 APFS 或其他現代高性能文件系統的塊數中獲得太多有用的資訊。如果您將文件壓縮到設備上,嘗試幾次以獲得最小版本可能會有所幫助,但否則不值得考慮。


總體而言,我們有一個編碼問題,使文件真正稍微變大,文件系統行為抵消了該問題,該行為略微修改了該較大文件的報告塊數,使其在您的測試中變小。真實大小測量顯示排序後持續增加 350 個字節。根據您的看法,這可能是 sort 中的錯誤,也可能是使用sort 時的錯誤,給它一個壞文件。

引用自:https://unix.stackexchange.com/questions/587019