유닉스 명령 줄의 파일에서 임의의 줄을 읽는 쉬운 방법은 무엇입니까?
유닉스 명령 줄의 파일에서 임의의 줄을 읽는 쉬운 방법은 무엇입니까?
답변:
당신은 사용할 수 있습니다 shuf
:
shuf -n 1 $FILE
라는 유틸리티도 있습니다 rl
. 데비안에서는 randomize-lines
모든 배포판에서 사용할 수는 없지만 원하는 것을 정확하게 수행 하는 패키지에 있습니다. 홈페이지에서 실제로 shuf
대신 사용하는 것이 좋습니다 (생성 될 때 존재하지 않았 음). shuf
GNU coreutils의 일부입니다 rl
.
rl -c 1 $FILE
shuf
팁에 감사합니다 . Fedora에 내장되어 있습니다.
sort -R
는 상당히 큰 파일 (80kk 라인)을 다룰 때 상당히 많은 시간을 기다리게 되지만, 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 one-liner는 이식성이 뛰어납니다 (내 테스트로는 약간 빠릅니다).
다른 대안 :
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 : 파일에서 임의의 줄을 어떻게 선택합니까? Camel Book의 저수지 샘플링 알고리즘은 다음과 같습니다.
perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file
이것은 전체 파일을 읽는 것보다 공간에서 중요한 이점이 있습니다.이 방법의 증거는 Donald E. Knuth의 3.4.2 절, 컴퓨터 프로그래밍 기술 (Art of Computer Programming, Volume 2)에서 확인할 수 있습니다.
shuf
. 펄 코드는 매우 약간 빠르지 만 (사용자 시간은 8 %, 시스템 시간은 24 % 더 빠릅니다), 펄 코드는 무작위로 "보이지 않는"것으로 나타났습니다 (주크 박스를 사용했습니다).
shuf
전체 입력 파일을 메모리에 저장합니다 . 끔찍한 생각입니다.이 코드는 한 줄만 저장 하므로이 코드의 한도는 INT_MAX (2 ^ 31 또는 2 ^ 63)입니다. 선택된 전 위선이 메모리에 맞는다고 가정합니다.
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}
단일 배쉬 라인 :
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()
i 범위 (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는 난수를 얻는 것보다 훨씬 비쌉니다. 내 코드는 파일을 한 번만 읽습니다. 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이 비어 있어도 작동하며,이 경우 줄이 생성되지 않습니다.)
이 방법의 한 가지 가능한 장점은 rand ()를 한 번만 호출한다는 것입니다.
의견에서 @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