良心又好用,Linux內置的shuf,解決你80%需要隨機的問題

引言

作為大多數熟練的 bash 程序員,都有可能沒有聽說過 shuf 這個指令。

不管你用或者不用,它都靜靜的躺在那裡,從Linux發行版開始,它已經內置在指令集裡了。

敢於冒險的人找到了 shuf,從此也改變了自己對他的看法。

良心又好用,Linux內置的shuf,解決你80%需要隨機的問題

shuf 是什麼

在本文中我們嘗試深入的瞭解 shuf 這個指令。

shuf是一個類似sort的命令行實用程序,包含在Coreutils中。您可能已經猜到,它用於偽隨機給定的輸入,就像您洗牌一樣。你猜對了!

字如其人,它的名稱也跟它的功能一樣一目瞭然。

找到了這個指令,那就用起來吧,像其他大多數指令一樣,在終端命令行裡用 --help 打印出它的可選項。

良心又好用,Linux內置的shuf,解決你80%需要隨機的問題


shuf在哪些領域比較有用呢?有三個方面。

  1. 文件 file。
  2. 列表 list。
  3. 區間 range。

每種方式都有其優點,提前預習下 shuf 指令的用法,可以儘量避免使用中出現的錯誤。


操作文件

文件操作是下坡最常用的方式。選項中包含 -e 或者 -i,默認為文件操作。也就是說,命令行告訴該指令要輸入的是一個文件。

文件來源可以是標準輸入,或者是手動指定的文件路徑。

參數列表的最後一個參數,也就可能是路徑名或文件名。如果省略參數,則視為從標準輸入讀取。

下面是一些示例,明確指定文件來源。


標準輸入隱式作為文件

這樣,我們就從shuf命令的參數中省略了file。根據約定,您可以選擇 - 來代替文件,以指示將該文件作為標準輸入。

<code>seq 3 | shuf/<code>

輸出內容為,

<code>1 3 2/<code>

標準輸入顯式作為文件

在命令行執行以下指令,

<code>seq 3 | shuf -/<code>

輸出內容如下,

<code>3 1 2/<code>

我們可以看到上述兩種方式。所達到的效果是一樣的。


明確指定文件名

通過這種方式,我們在shuf命令的參數中指定一個文件名作為文件。下面是幾個使用文件的文件shuf示例。

從終端輸入

執行以下指令,

<code>shuf /dev/fd/1/<code>
良心又好用,Linux內置的shuf,解決你80%需要隨機的問題

/dev/fd/1 其實就是類UNIX系統中的標準輸入。命令行 Enter 之後。會停留在輸入窗口。如上圖所示,輸入任意字符串。最後按 Ctrl + D 結束輸入。shuf 將輸入的文本隨機打斷並輸出。


打亂文件的行

輸入以下指令,

<code>seq 3 > tmp.txt
shuf tmp.txt
rm -f tmp.txt/<code>

輸出內容如下,

<code>2 1 3/<code>


列表

在這種方式中,我們操作一個文件或通過管道輸入到shuf命令中。通過這種方式,我們允許使用 -e 選項將輸入行指定為shuf命令的參數,從而強制 shuf 作為列表 shuf 進行操作。

用法如下,

<code>Usage: shuf -e [OPTION]... [ARG].../<code>

在終端輸入一些命令,

<code>shuf -e 1 2 3/<code>

輸出內容如下,

<code>1 3 2/<code>

上述指令作用等同於

<code>seq 3 | shuf -/<code> 

使用變量作為參數

在終端輸入以下指令,

<code>var="1 2 3";
shuf -e ${var}/<code>

這個沒有這個例子很簡單,就是使用了 bash 變量進行操作。變量中存儲了一個列表。

當然了,生成列表也可以用 bash 內置的方式。

<code>shuf -e {1..3}/<code>

輸出內容如下,

<code>1 2 3/<code>

bash 的一些其他玩法,

<code>shuf -e $( seq 3 )/<code>

本質上與命令符、管道、重定向,或文件內容讀取原理是一致的。


區間

最後一種方法與前面介紹的方法不同。它不需要在命令行中指定文件或參數,而是需要一個整數範圍。-i 選項強制 shuf 作為 range shuf 操作。

區間 shuf 生成一個按隨機順序排列的整數範圍。

用法如下,

<code>Usage: shuf -i LO-HI [OPTION].../<code>

先舉一個例子,

<code>shuf -i 1-3/<code>

輸出內容如下,

<code>2 3 1/<code>


一些高級選項

下面列出的這些高級選項,在 bash 腳本編程中可能會很有用。

限制輸出行數

運行以下指令,

<code>shuf -i 1-3 -n 1/<code>

輸出內容如下,

<code>3/<code>

我們使用參數 -n 指定輸出的行數。本例中 -n 等於 1,那麼僅輸出一行。

指定輸出文件

像其他一些Linux中的指令一樣,-o 用於指定輸出文件名。舉例說明,

<code>shuf -i 1-3 -n 1 -o filter.txt
cat filter.txt
rm -f filter.txt/<code>

輸出內容如下,

<code>1/<code>


流操作

為了創建連續的輸出行流,我們使用-r選項,如下所示。

<code>shuf -e {0,1} -r | xargs -i echo -n "{}"/<code>

輸出內容如下,

<code>000101101010101101010110000101111010001010111001110…/<code>


使用\\0代替\\n用作分隔符

使用\\0作為分隔行,僅需要使用 -z 選項。舉例如下:

<code>seq 3 | tr '\\n' '\\0' | shuf -z/<code>

輸出內容如下:

<code>213/<code> 


如果沒有shuffle,bash隨機函數長什麼樣?

下面使用 gawk 實現類似 shuf 的功能,我們看看區別在哪裡。

閒言少敘,直接上腳本。

<code>gawk-shuf() {
gawk -v random=${RANDOM} '
function randInt() {
return int(rand()*1000)
}
function case_numeric_compare(i1, v1, i2, v2, l, r) {
l = int(v1)
r = int(v2)
if(l else if(l==r) return 0
else return 1
}
BEGIN {
count=1
srand(random)
}
{
rank[count]=randInt()
line[count]=$(0)
count++
}
END {
asorti(rank,order,"case_numeric_compare")
for(i=0;i<count> print line[order[i]]
}
}
' -
}
if [ ${#} -eq 0 ]
then
true
else
exit 1 # wrong args
fi
gawk-shuf/<count>
/<code>

功能是一樣的,但是效率很低,維護起來很麻煩,對不對?


寫在最後

大神們都已經準備好工具了,我們只用把它拿出來使用,不要重複造輪子了。

Happy coding :)


分享到:


相關文章: