我有一個文字文件,其中有分隔文字區塊的空白行。我想使用 *NIX 命令列工具來打亂此文件,同時尊重區塊結構。換句話說,在輸出中我想看到區塊的順序改變了;區塊內的行及其順序保持不變。
輸入檔案範例:
line 1
line 2
line 10
line 20
line 30
line 100
line 200
輸出檔案(隨機播放後):
line 10
line 20
line 30
line 1
line 2
line 100
line 200
當然,重複運行應該給出不同的區塊順序。
文件的第一行始終非空。沒有雙空行。文件的最後一行始終為空。
我編寫了一個非常簡單的 Python 腳本,它讀取列表列表中的所有行,對其進行打亂並輸出。我很好奇是否可以使用標準的 *NIX 工具來做到這一點。
答案1
POSIXly,你可以這樣做:
<file awk '
BEGIN{srand(); n=rand()}
{print n, NR, $0}
!NF {n=rand()}
END {if (NF) print n, NR+1, ""}' |
sort -nk1 -k2 |
cut -d' ' -f3-
也就是說,在每行前面加上<a-random-number-that-changes-with-each-paragraph>
行號,然後對第一個數字進行數字排序,然後對第二個數字進行排序,以保持段落中的行順序並刪除那些多餘的數字。
人們可能需要透過管道來sed '$d'
刪除尾隨的空白行。
請注意,大多數awk
實作 srand()
都使用 unix 紀元時間來為偽隨機數產生器提供種子,因此如果在同一秒內運行兩次,您可能會得到相同的結果(a不幸的是,儘管我付出了努力,但歷史錯誤現在已刻在 POSIX 規範中)。
答案2
使用 GNU 工具,這會將段落分成以 NUL 分隔的群組,對它們進行打亂,然後刪除 NUL:
$ sed '1s/^/\n/; s/^$/\x00/' input | shuf -z | sed '1d; s/\x00//'
line 100
line 200
line 10
line 20
line 30
line 1
line 2
不使用 NUL 的替代方法
由於並非所有工具都支援 NUL 字符,因此這裡有一個替代方案。這會讀取段落,替換~
換行符,然後隨機播放,然後~
在顯示結果之前將其轉換回換行符:
$ awk '{gsub(/\n/, "~")} 1' RS= input | shuf | awk '{gsub(/~/, "\n")} 1' ORS="\n\n"
line 10
line 20
line 30
line 100
line 200
line 1
line 2
如果您的文字可能包含~
,則使用文字不包含的另一個字元作為臨時行分隔符號。
答案3
使用perl:
perl -MList::Util -00 -e 'chomp(my @a=<>); print join("\n\n", List::Util::shuffle @a) . "\n";' < input
或作為腳本文件展開:
#!/usr/bin/perl
use List::Util 'shuffle';
local $/ = ""; ## paragraph mode
chomp(my @a = <>);
print join("\n\n", shuffle @a) . "\n";