Unixコマンドラインでファイルからランダムな行を読み取る簡単な方法は何ですか?
Unixコマンドラインでファイルからランダムな行を読み取る簡単な方法は何ですか?
回答:
使用できますshuf
:
shuf -n 1 $FILE
と呼ばれるユーティリティもありますrl
。Debianではrandomize-lines
、すべてのディストリビューションで利用できるわけではありませんが、まさにそれはあなたが望むものを実行するパッケージにあります。そのホームページでは、実際にshuf
代わりに(作成時に存在しなかった)の使用を推奨しています。 shuf
GNU coreutilsの一部ですが、そうでrl
はありません。
rl -c 1 $FILE
shuf
ヒントをありがとう、それはFedoraに組み込まれています。
sort -R
は、必ずかなり待機することになりますが、shuf -n
非常に瞬時に動作します。
coreutils
Homebrewからインストールすると、OS Xでshufを取得できます。のgshuf
代わりに呼び出される場合がありshuf
ます。
randomize-lines
OS Xでも使用できますbrew install randomize-lines; rl -c 1 $FILE
shuf
はGNU Coreutilsの一部であるため、* BSDシステム(またはMac?)では必ずしも(デフォルトで)使用できるとは限らないことに注意してください。以下の@ Tracker1のperlワンライナーは、より移植性があります(私のテストでは、わずかに高速です)。
別の選択肢:
head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1
(${RANDOM} << 15) + ${RANDOM}
ます。これにより、バイアスが大幅に軽減され、最大10億行を含むファイルで機能するようになります。
+
そして、|
同じであるので${RANDOM}
、定義により0..32767です。
sort --random-sort $FILE | head -n 1
(私は上記のshufアプローチがさらに好きです-それが存在することさえ知らなかったし、自分でそのツールを見つけることはなかったでしょう)
sort
、ご使用のシステム(CentOS 5.5、Mac OS 10.7.2)で動作しない最新のが必要な場合があります。また、猫の無駄な使用は次のように減らすことができますsort --random-sort < $FILE | head -n 1
sort -R <<< $'1\n1\n2' | head -1
sort -R
重複する行を一緒にソートするため、1と2を返す可能性が高くなります。同じことがsort -Ru
、重複する行を削除するため、にも当てはまります。
sort
にパイプする前にファイル全体をシャッフルする必要があるため、これは比較的低速head
です。shuf
代わりに、ファイルからランダムな行を選択し、私にとってははるかに高速です。
sort --random-sort $FILE | head
使用すると、ファイルに直接アクセスでき、効率的な並列ソートが可能になる可能性があるため、
--random-sort
および-R
オプションは、ソートGNU(彼らはBSDやMac OSで動作しないように固有のものですsort
)。GNUソートはこれらのフラグを2005年に学習したため、GNU coreutils 6.0以降(CentOS 6など)が必要です。
perlfaq5:ファイルからランダムな行を選択するにはどうすればよいですか?ラクダ本のリザーバーサンプリングアルゴリズムを次に示します。
perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file
これは、ファイル全体を読み取るよりもスペースの点で大きな利点があります。この方法の証拠は、Donald E. KnuthによるThe Art of Computer Programming、Volume 2、Section 3.4.2にあります。
shuf
。perlコードは非常にわずかに高速です(ユーザー時間で8%高速、システム時間で24%高速)。
shuf
入力ファイル全体をメモリに保存しますが、これは恐ろしい考えです。したがって、このコードの制限はINT_MAXの行数です(2 ^ 31または2 ^ 63 arch)、その選択された潜在的な行のいずれかがメモリに収まると仮定します。
bashスクリプトを使用する:
#!/bin/bash
# replace with file to read
FILE=tmp.txt
# count number of lines
NUM=$(wc - l < ${FILE})
# generate random number in range 0-NUM
let X=${RANDOM} % ${NUM} + 1
# extract X-th line
sed -n ${X}p ${FILE}
単一のbash行:
sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt
わずかな問題:ファイル名が重複しています。
wc -l < test.txt
にパイプする必要がなくなりcut
ます。
これがその仕事をする簡単なPythonスクリプトです:
import random, sys
lines = open(sys.argv[1]).readlines()
print(lines[random.randrange(len(lines))])
使用法:
python randline.py file_to_get_random_line_from
import random, sys lines = open(sys.argv[1]).readlines()
for i in range(len(lines)):rand = random.randint(0、len(lines)-1)print lines.pop(rand)、
len(lines)
、IndexErrorが発生する可能性があります。使用できますprint(random.choice(list(open(sys.argv[1]))))
。また、メモリ効率の高いリザーバーサンプリングアルゴリズムもあります。
「awk」を使用する別の方法
awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name
wc
、行数を取得するためにファイルを読み取る()必要があります。次に、ファイル(の一部awk
)を再度読み取り()、指定されたランダムな行番号の内容を取得する必要があります。I / Oは、乱数を取得するよりもはるかにコストがかかります。私のコードはファイルを1回だけ読み取ります。awkの問題rand()
は、秒単位でシードするため、連続して実行しすぎると重複が発生することです。
MacOSXでも動作し、Linux(?)でも動作するソリューション:
N=5
awk 'NR==FNR {lineN[$1]; next}(FNR in lineN)' <(jot -r $N 1 $(wc -l < $file)) $file
どこ:
N
必要なランダム線の数です
NR==FNR {lineN[$1]; next}(FNR in lineN) file1 file2
->書き込まれた行番号を保存してfile1
から、対応する行を印刷するfile2
jot -r $N 1 $(wc -l < $file)
-> で範囲内のN
数値をランダムに(-r
)描画(1, number_of_line_in_file)
しjot
ます。プロセスの置換<()
により、インタプリタのファイルのように見えるのでfile1
、前の例では。#!/bin/bash
IFS=$'\n' wordsArray=($(<$1))
numWords=${#wordsArray[@]}
sizeOfNumWords=${#numWords}
while [ True ]
do
for ((i=0; i<$sizeOfNumWords; i++))
do
let ranNumArray[$i]=$(( ( $RANDOM % 10 ) + 1 ))-1
ranNumStr="$ranNumStr${ranNumArray[$i]}"
done
if [ $ranNumStr -le $numWords ]
then
break
fi
ranNumStr=""
done
noLeadZeroStr=$((10#$ranNumStr))
echo ${wordsArray[$noLeadZeroStr]}
私のMac OSは簡単な答えをすべて使用しているわけではないので、これが私が発見したものです。$ RANDOM変数ソリューションはテストではあまりランダムではないようなので、jotコマンドを使用して数値を生成しました。私のソリューションをテストするとき、出力で提供されるソリューションに大きなばらつきがありました。
RANDOM1=`jot -r 1 1 235886`
#range of jot ( 1 235886 ) found from earlier wc -w /usr/share/dict/web2
echo $RANDOM1
head -n $RANDOM1 /usr/share/dict/web2 | tail -n 1
変数のエコーは、生成された乱数のビジュアルを取得することです。
バニラsedとawkのみを使用し、$ RANDOMを使用しない場合、FILENAMEという名前のファイルから単一行を擬似ランダムに選択するためのシンプルでスペース効率が高く、適度に高速な「ワンライナー」は次のとおりです。
sed -n $(awk 'END {srand(); r=rand()*NR; if (r<NR) {sub(/\..*/,"",r); r++;}; print r}' FILENAME)p FILENAME
(これは、FILENAMEが空の場合でも機能します。その場合、行は出力されません。)
このアプローチの考えられる利点の1つは、rand()を1回しか呼び出さないことです。
コメントで@AdamKatzが指摘したように、別の可能性は各行に対してrand()を呼び出すことです。
awk 'rand() * NR < 1 { line = $0 } END { print line }' FILENAME
(帰納法に基づいて、正当性の簡単な証明を与えることができます。)
rand()
「gawkを含むほとんどのawk実装では、rand()はawkを実行するたびに、同じ開始番号またはシードから番号の生成を開始します。」
- https://www.gnu.org/software/gawk/manual/html_node/Numeric-Functions.html