유닉스 명령 줄의 파일에서 임의의 줄을 읽는 쉬운 방법은 무엇입니까?


답변:


383

당신은 사용할 수 있습니다 shuf:

shuf -n 1 $FILE

라는 유틸리티도 있습니다 rl. 데비안에서는 randomize-lines모든 배포판에서 사용할 수는 없지만 원하는 것을 정확하게 수행 하는 패키지에 있습니다. 홈페이지에서 실제로 shuf대신 사용하는 것이 좋습니다 (생성 될 때 존재하지 않았 음). shufGNU coreutils의 일부입니다 rl.

rl -c 1 $FILE

2
shuf팁에 감사합니다 . Fedora에 내장되어 있습니다.
Cheng

5
Andalso sort -R는 상당히 큰 파일 (80kk 라인)을 다룰 때 상당히 많은 시간을 기다리게 되지만, shuf -n매우 즉각적으로 작동합니다.
Rubens

23
coreutilsHomebrew에서 설치하여 OS X에서 shuf를 얻을 수 있습니다 . gshuf대신에 호출 될 수 있습니다 shuf.
Alyssa Ross

2
마찬가지로, 다음 randomize-lines과 같이 OS X 에서도 사용할 수 있습니다 .brew install randomize-lines; rl -c 1 $FILE
Jamie

4
참고 shuf의 일부 GNU로 coreutils 그러므로와 반드시 * BSD 시스템 (기본적으로) 사용할 수 없습니다 (또는 Mac?). 아래의 @ Tracker1의 perl one-liner는 이식성이 뛰어납니다 (내 테스트로는 약간 빠릅니다).
Adam Katz

74

다른 대안 :

head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1

28
$ {RANDOM}은 32768 미만의 숫자 만 생성하므로 큰 파일 (예 : 영어 사전)에는이 값을 사용하지 마십시오.
Ralf

3
이것은 모듈로 연산으로 인해 모든 라인에 대해 동일한 확률을 제공하지는 않습니다. 파일 길이가 << 32768 (그리고 그 숫자를 나누면 전혀 아님)이면 거의 문제가되지 않지만 주목할 가치가 있습니다.
Anaphory

10
을 사용하여 이것을 30 비트 난수로 확장 할 수 있습니다 (${RANDOM} << 15) + ${RANDOM}. 이것은 바이어스를 크게 줄이고 최대 10 억 라인을 포함하는 파일에서 작동 할 수있게합니다.
nneonneo 2016 년

@nneonneo :이 링크에 따라이 $ {RANDOM을} OR 처리해야하지만 아주 대신 PLUS'ing의이야 ', 트릭을 냉각 stackoverflow.com/a/19602060/293064
제이 테일러

+그리고 |이후 동일한 ${RANDOM}정의에 의해 0..32767입니다.
nneonneo

71
sort --random-sort $FILE | head -n 1

(나는 위의 shuf 접근법을 더 좋아한다. 나는 그것이 존재한다는 것을 몰랐고 나는 그 도구를 내 자신에서 결코 찾지 못했을 것이다)


10
+1 마음에 들지만 최신 sort시스템 이 필요 하지 않을 수도 있습니다 (CentOS 5.5, Mac OS 10.7.2). 또한, 고양이의 불필요한 사용은 감소 될 수있다sort --random-sort < $FILE | head -n 1
스티브 Kehlet

sort -R <<< $'1\n1\n2' | head -1sort -R정렬은 중복 행을 함께 정렬 하므로 1과 2를 반환 할 가능성이 높습니다 . sort -Ru중복 행을 제거하기 때문에에 적용됩니다 .
Lri

5
전체 파일을 sort로 파이프하기 전에 셔플해야하므로 상대적으로 느립니다 head. shuf대신 파일에서 임의의 줄을 선택하면 훨씬 빠릅니다.
Bengt

1
@SteveKehlet sort --random-sort $FILE | head은 파일에 직접 액세스하여 효율적인 병렬 정렬을 가능하게하므로 파일에 직접 액세스 할 수 있으므로 가장 좋습니다.
WaelJ

5
--random-sort-R옵션은 GNU의 종류에 따라 다릅니다 (그들은 BSD 또는 Mac OS에서는 작동하지 않습니다 그래서 sort). GNU 정렬은 2005 년에 이러한 플래그를 배웠으므로 GNU coreutils 6.0 이상 (예 : CentOS 6)이 필요합니다.
RJHunter

31

이것은 간단합니다.

cat file.txt | shuf -n 1

이것은 "shuf -n 1 file.txt"자체보다 조금 느리다는 것을 알 수 있습니다.


2
가장 좋은 답변입니다. 나는이 명령에 대해 몰랐다. 한 -n 1줄 을 지정하고 1 줄 이상으로 변경할 shuf수 있습니다. 다른 용도로도 사용할 수 있습니다. 방금 파이프 ps aux하고grep 사용하여 이름과 부분적으로 일치하는 프로세스를 임의로 종료했습니다.
sudo

18

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)에서 확인할 수 있습니다.


1
포함 목적으로 (참조 된 사이트가 다운되는 경우) Tracker1이 가리키는 코드는 다음과 같습니다. "cat filename | perl -e 'while (<>) {push (@ _, $ _);} print @ _[랜드()*@_];';"
Anirvan

3
이것은 쓸모없는 고양이의 사용입니다. 다음은 perlfaq5 (및 Camel book에서 제공)에있는 코드를 약간 수정 한 것입니다. perl -e 'srand; rand ($.) <1 && ($ line = $ _) while <>; $ line 인쇄; ' 파일 이름
Mr. Muskrat

오류 ... 링크 된 사이트, 즉
Nathan Fellman

방금이 코드의 N 라인 버전을 벤치마킹했습니다 shuf. 펄 코드는 매우 약간 빠르지 만 (사용자 시간은 8 %, 시스템 시간은 24 % 더 빠릅니다), 펄 코드는 무작위로 "보이지 않는"것으로 나타났습니다 (주크 박스를 사용했습니다).
Adam Katz

2
더 많은 생각을하십시오 : shuf전체 입력 파일을 메모리에 저장합니다 . 끔찍한 생각입니다.이 코드는 한 줄만 저장 하므로이 코드의 한도는 INT_MAX (2 ^ 31 또는 2 ^ 63)입니다. 선택된 전 위선이 메모리에 맞는다고 가정합니다.
Adam Katz

11

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}

1
랜덤은 0 일 수 있으며, sed는 첫 번째 라인에 1이 필요합니다. sed -n 0p는 오류를 반환합니다.
asalamon74

"tmp.txt"의 경우 $ 1, NUM의 경우 $ 2는 어떻습니까?
blabla999

그러나 펄이나 파이썬이 필요없고 얻을 수있는만큼 효율적이기 때문에 포인트가 가치가있는 버그조차도 있습니다 (파일을 정확하게 두 번 읽지 만 메모리로 읽지 마십시오-따라서 거대한 파일에서도 작동합니다).
blabla999

@ asalamon74 : 감사합니다 @ blabla999 : 우리가 함수를 만들면 1 달러에 괜찮습니다. 그런데 왜 NUM을 계산하지 않습니까?
Paolo Tedesco

sed 행을 다음과 같이 변경하십시오. head-$ {X} $ {FILE} | tail -1해야 할 것
JeffK

4

단일 배쉬 라인 :

sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt

약간의 문제 : 파일 이름이 중복되었습니다.


2
약간의 문제. / usr / share / dict / words에서 이것을 수행하는 것은 "A"로 시작하는 단어를 선호하는 경향이 있습니다. 그것과 함께, 나는 약 90 % "A"단어에서 10 % "B"단어입니다. 아직 숫자로 시작하지 않아 파일의 헤드를 구성합니다.
bibby

wc -l < test.txt에 배관 할 필요가 없습니다 cut.
fedorqui 'SO 중지 피해'

3

다음은 작업을 수행하는 간단한 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

1
이것은 작동하지 않습니다. 한 줄 후에 멈 춥니 다. 그것을 작동시키기 위해, 나는 이렇게했다 : import random, sys lines = open(sys.argv[1]).readlines() i 범위 (len (lines)) : rand = random.randint (0, len (lines) -1) print lines.pop (rand),
Jed Daniels

엉터리 형식의 어리석은 주석 시스템. 댓글에서 형식이 한 번에 작동하지 않았습니까?
Jed Daniels

randint는 포괄적이므로 len(lines)IndexError가 발생할 수 있습니다. 사용할 수 있습니다 print(random.choice(list(open(sys.argv[1])))). 메모리 효율적인 저장소 샘플링 알고리즘도 있습니다.
jfs

2
배고픈 공간. 3TB 파일을 고려하십시오.
Michael Campbell

@ MichaelCampbell : 위에서 언급 한 저수지 샘플링 알고리즘 은 3TB 파일에서 작동 할 수 있습니다 (라인 크기가 제한적 인 경우).
jfs

2

' awk '를 사용하는 다른 방법

awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name

2
용도 awk가 배시 (즉, $RANDOMA는 bashism ). 다음은 위의 Tracker1의 인용 perlfaq5 코드는 @와 같은 논리를 사용하여 순수 AWK (mawk의) 방법 : awk 'rand() * NR < 1 { line = $0 } END { print line }' file.name(와우, 심지어의 짧은 펄 코드보다가!)
아담 카츠

해당 코드는 wc줄 수를 얻기 위해 파일 ( )을 읽은 다음 파일을 다시 읽고 (일부) awk주어진 임의의 줄 번호의 내용을 가져와야 합니다 ( ). I / O는 난수를 얻는 것보다 훨씬 비쌉니다. 내 코드는 파일을 한 번만 읽습니다. awk의 문제 rand()는 초 단위로 시드되므로 연속적으로 너무 빨리 실행하면 복제본이 생성된다는 것입니다.
Adam Katz

1

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이전 예제에서 인터프리터의 파일처럼 보이게합니다 .

0
#!/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]}

$ RANDOM은 / usr / share / dict / words에 235886이있는 / usr / share / dict / words의 단어 수보다 적은 수를 생성하므로 0과 9 사이에 6 개의 분리 된 난수를 생성하고 함께 묶습니다. 그런 다음 숫자가 235886보다 작은 지 확인하십시오. 그런 다음 선행 0을 제거하여 배열에 저장된 단어를 인덱싱하십시오. 각 단어는 자체 줄이므로 임의의 파일에서 임의로 줄을 선택하는 데 쉽게 사용할 수 있습니다.
Ken

0

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

변수의 에코는 생성 된 난수를 표시하는 것입니다.


0

바닐라 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


sed를 요구하지 않는 더 간단한 awk 솔루션이있는 이 답변 전에 1 년 전에 게시 한 의견을 참조하십시오 . 또한 awk의 난수 생성기에 대한 경고에 유의하십시오.
Adam Katz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.