我剛剛遇到一個問題,必須從一個大(千兆字節)大小的文件中剪切一些行,並且意識到潛在的CPU 佔用試圖在內存中讀取它,我想就地編輯它... ……所以遇到了這些問題:
……還有這些:
然而,我在思考其他事情:我相信(但我不確定)任何文件系統(例如ext3
)都必須使用類似連結列表的東西,以便能夠描述類似文件片段之類的東西映射到磁碟區域。
因此,應該可以做這樣的事情 - 例如,假設我有一個bigfile.dat
這樣的文件(數字應該表示位元組偏移量,但對齊它們有點困難):
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
L 1\n L 2\n L 3\n L 4\n L 5\n L 6\n
原則上,該檔案可以載入到終端應用程式中進行瀏覽 - 假設我們呼叫一個 tool editsegments bigfile.dat
,並且假設它類似於less -N bigfile.dat
顯示相同檔案(帶有行號)的方式:
1 1 L 1
2 2 L 2 *
3 3 L 3
4 4 L 4 *
5 5 L 5
6 6 L 6
bigfile.dat (END)
比方說,我可以在那裡輸入一個命令(例如d
刪除行),單擊另一個鍵或滑鼠,上面顯示*
- 意味著第 2 行和第 4 行之間的所有內容都應該被刪除。然後程式將做出回應並顯示以下內容:
1 1 L 1
2 5 L 5
3 6 L 6
bigfile.dat (END)
現在我們可以看到最左邊的第一列顯示「新」行號(剪下後),第二列是「舊」行號(剪下前) - 然後是實際的行內容。
現在,我想像在這個偽應用程式editsegments
退出後會發生什麼,首先也是最重要的是,bigfile.dat
它沒有受到影響;但是,現在同一目錄中還會有一個額外的文字文件,例如bigfile.dat.segments
;包含這些內容:
d 4:15 # line 2-4
bigfile.dat.iedit
……此外,還會出現一個特殊文件(如「符號連結」)-我們稱之為「它」。
現在,基本上,所有這一切的結果將是,如果我現在嘗試bigfile.dat.iedit
使用類似的東西打開less -N bigfile.dat.iedit
,我想獲得“編輯”的內容:
1 L 1
2 L 5
3 L 6
bigfile.dat (END)
……我想,這可以透過以某種方式指示作業系統來實現,即當$FILE.iedit
開啟時,首先$FILE.segments
應該打開並讀取;將d 4:15
指示原始文件中的位元組 4 到 15 應被省略 - 這將導致類似以下內容的結果:
0 1 2 3 4 5 6 7 8 9 10 11 12,3,4 15 16 17 18 19 20 21 22 23
L 1\n
L
2
\n
L
3
\n
L
4
\n
L 5\n
L 6\n
0 1 2 3 ------------------------------------------>16 17 18 19 20 21 22 23
換句話說 -假設在文件的文件系統概念中,內容的每個字節還包含到鏈中下一個字節的“鏈接” - 應該可以指示文件系統基於腳本建立新的鍊錶,並提供內容如通過特殊文件(符號鏈接或管道)修改後的鍊錶所表示的。
這就是我在標題中所說的「腳本化」的意思——「新」鍊錶可以由腳本文件($FILE.segments
)控制,用戶可以在文字編輯器中編輯(或由前端應用程式生成)。我所說的「多次」是指bigfile.dat
在這個過程中根本沒有修改;所以我今天可以編輯第一個(原始)千兆字節,將進度保存在 ( $FILE.segments
) 中 - 然後我可以明天編輯第二個千兆字節,再次將進度保存在 ( $FILE.segments
) 中,等等- 一直以來,原始內容bigfile.dat
都沒有改變。
當所有編輯完成後,人們可能會調用某種命令(例如,editsegments --finalize bigfile.dat
),它將簡單地將新連結列表永久編碼為 的內容bigfile.dat
(並與此一致,刪除bigfile.dat.segments
和bigfile.dat.iedit
)。或者更簡單,人們可以這樣做:
cp bigfile.dat.iedit /path/to/somewhere/else/bigfile.modified.dat
當然,除了d
elete 腳本指令之外,r
還可以有 eplace 指令,例如:
r 16:18 AAA
....說:將位元組 16 和 18 之間的內容替換為空格後的下一個 18-16+1=3 個位元組(即AAA
) - 鍊錶實際上可以“掛鉤”到腳本命令內容本身(下圖還包含d
elete):
0 1 2 3 4 5 6 7 8 9 10 11 12,3,4 15 16 17 18 19 20 21 22 23
L 1\n
L
2
\n
L
3
\n
L
4
\n
L
5
\n
L 6\n
0 1 2 3 ------------------------------------------>| | 19 20 21 22 23
.
.
.
.
.
.
.
\n
r
1
6
:
1
8
AAA
\n
.
.
.
.
現在,我猜想程式就像hexedit
(如上所述這裡)確實就地更改文件 - 但我只是喜歡腳本編寫可能性的好處(如果它可以由 GUI 應用程序調節,即使是終端應用程序,那就更好了),以及實際上沒有原始文件的好處更改,直到確認所有編輯均符合要求。
我不確定這樣的事情是否可能 - 即使是,我想它可能需要一個專用的驅動程式(而不僅僅是一個用戶程式)......但我想無論如何還是值得一問 - 是否有Linux上有類似的東西嗎?
非常感謝您的回答,
乾杯!
答案1
磁碟上檔案的結構取決於所使用的檔案系統。現實世界的檔案系統都不使用您所描述的連結清單(這會讓人fseek(3)
難以忍受)。與此最接近的是微軟的胖的,本質上是將指標從資料塊移到一個陣列中來隱藏它們。
但大多數檔案系統確實使用一些對檔案中資料區塊的基於指標的引用,因此原則上,只需打亂少量指標(而不是整個檔案內容)並在檔案中標記一個區塊,就可以剪切文件區塊。遺憾的是,這不是一個非常有用的操作,文件區塊相當大(通常為 4KiB),很少與文件中的結構(無論是行還是其他細分)合理對齊。
答案2
你所描述的聽起來很像重播一個文字編輯器的重做列表針對未更改的原始文件重做列表屬於.我很確定gvim
有這樣一個堅持不懈的撤消/重做列表,您可以(?)能夠使用它,而且我知道emacs
肯定有這樣一個列表,您很可能可以哄騙它做任何您想做的事情(通過腳本elisp
),例如。儲存會話之間的 Emacs 撤銷歷史記錄。
作為旁注,對於如此大的文件,關閉所有不需要的操作可能是一個好主意,例如:自動儲存,語法高亮顯示(慢速上大的emacs 檔案)等,32 位元系統上的 emacs 有 256 MB檔案大小限制。
它當然不會像您所建議的那樣簡潔,但如果沒有大量更改,則可能有用。
答案3
通常,如果不將整個文件放入內存,則無法就地編輯文件。我假設您真正想要做的只是擁有一個新文件,它是舊文件的副本,沒有特定的行。這可以使用 unix 實用程式head
和輕鬆完成tail
。例如,要從文件中複製除第 5、12 和 52 行之外的所有內容,您可以執行下列操作
head -n 4 bigfile.dat > tempfile.dat
tail -n +6 bigfile.dat | head -n 6 >> tempfile.dat
tail -n +13 bigfile.dat | head -n 39 >> tempfile.dat
tail -n 53 bigfile.dat >> tempfile.dat
如果您不熟悉這些實用程序,我將更詳細地解釋。
此head
實用程式會列印出文件中的前 n 行。如果沒有給出位置參數,它將使用標準輸入作為文件。該-n
標誌告訴 head 要列印多少行。因此,head -n 2
將僅列印標準輸入的前兩行。
該tail
實用程式會列印出文件的最後 n 行。與 head 一樣,它可以從檔案或標準輸入中讀取。 -n 標誌告訴 tail 從最後開始列印多少行。您還可以在數字前面加上加號,以告訴 tail 從文件末尾開始列印從開頭開始的那麼多行。例如,tail -n 2
列印標準輸入的最後兩行。但tail -n +2
列印出從第 2 行開始的所有行(省略第 1 行)。
所以一般來說,如果你想從文件中列印 [x, y) 範圍內的行,你會這樣做
`tail -n +x | head -n d`
其中 d = y - x。這些命令將產生一個新檔案。如果您願意,您可以刪除舊文件。這樣做的好處是,每次只需要在記憶體中保留一行,因此它不會很快填滿您的 RAM head
。tail
答案4
聽起來像是 sed 腳本的工作。 IIRC 它是為此類任務而設計的。逐行處理、同一組命令的重複處理以及正規表示式都合併在一個工具中。雖然我知道它會完成這項工作,但除了指導您完成任務之外,我無法進一步指導您手冊頁。