高效合併/排序/唯一的大量文本文件
我正在嘗試一個天真的:
$ cat * | sort -u > /tmp/bla.txt
失敗了:
-bash: /bin/cat: Argument list too long
因此,為了避免像(創建一個巨大的臨時文件)這樣的愚蠢解決方案:
$ find . -type f -exec cat {} >> /tmp/unsorted.txt \; $ cat /tmp/unsorted.txt | sort -u > /tmp/bla.txt
我雖然可以使用(這應該減少記憶體消耗,並且更接近流機制)來處理文件:
$ cat proc.sh #!/bin/sh old=/tmp/old.txt tmp=/tmp/tmp.txt cat $old "$1" | sort -u > $tmp mv $tmp $old
緊隨其後的是:
$ touch /tmp/old.txt $ find . -type f -exec /tmp/proc.sh {} \;
有沒有更簡單的unix風格的替換:
cat * | sort -u
當文件數量達到時MAX_ARG
?為這樣一個常見的任務編寫一個小的 shell 腳本感覺很尷尬。
使用 GNU
sort
和一個printf
內置的外殼(現在所有類似 POSIX 的外殼,除了 的某些變體pdksh
):printf '%s\0' * | sort -u --files0-from=- > output
現在,一個問題是因為該管道的兩個組件是同時且獨立地執行的,當左邊的組件擴展
*
glob 時,右邊的組件可能已經創建了output
文件,這可能會導致問題(可能不是-u
這裡)就像output
輸入和輸出文件一樣,因此您可能希望將輸出轉到另一個目錄(> ../output
例如),或者確保 glob 與輸出文件不匹配。在這種情況下解決它的另一種方法是編寫它:
printf '%s\0' * | sort -u --files0-from=- -o output
這樣,它就
sort
可以output
編寫並且(在我的測試中),它在收到完整的文件列表之前不會這樣做(在 glob 被擴展之後很長時間)。output
如果沒有一個輸入文件是可讀的,它也將避免崩潰。另一種用
zsh
or寫的方法bash
sort -u --files0-from=<(printf '%s\0' *) -o output
那是使用程序替換(其中
<(...)
被替換為指向管道讀取端正在printf
寫入的文件路徑)。該功能來自ksh
,但ksh
堅持將<(...)
單獨的參數擴展為命令,因此您不能將其與--option=<(...)
語法一起使用。不過,它可以使用這種語法:sort -u --files0-from <(printf '%s\0' *) -o output
請注意,
cat
在某些文件不以換行符結尾的情況下,您會看到與提供文件輸出的方法不同:$ printf a > a $ printf b > b $ printf '%s\0' a b | sort -u --files0-from=- a b $ printf '%s\0' a b | xargs -r0 cat | sort -u ab
另請注意,
sort
使用區域設置 ( ) 中的排序規則算法進行排序strcollate()
,並sort -u
報告按該算法排序相同的每組行中的一個,而不是字節級別的唯一行。如果您只關心行在字節級別上的唯一性並且不太關心它們的排序順序,您可能希望將語言環境修復為 C,其中排序基於字節值 (memcmp()
; 這可能會加快事情顯著):printf '%s\0' * | LC_ALL=C sort -u --files0-from=- -o output
一個簡單的修復,至少在 Bash 中有效,因為
printf
它是內置的,並且命令行參數限制不適用於它:printf "%s\0" * | xargs -0 cat | sort -u > /tmp/bla.txt
(
echo * | xargs
也可以,除了處理帶有空格的文件名等)