Shell

高效合併/排序/唯一的大量文本文件

  • January 1, 2020

我正在嘗試一個天真的:

$ 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 腳本感覺很尷尬。

使用 GNUsort和一個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如果沒有一個輸入文件是可讀的,它也將避免崩潰。

另一種用zshor寫的方法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也可以,除了處理帶有空格的文件名等)

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