쉘 스크립트를 작성하려고합니다. 아이디어는 텍스트 파일에서 무작위로 한 줄을 선택하여 우분투 데스크탑 알림으로 표시하는 것입니다.
그러나 스크립트를 실행할 때마다 다른 줄을 선택하고 싶습니다. 이를위한 해결책이 있습니까? 전체 스크립트를 원하지 않습니다. 그 단순한 것만.
쉘 스크립트를 작성하려고합니다. 아이디어는 텍스트 파일에서 무작위로 한 줄을 선택하여 우분투 데스크탑 알림으로 표시하는 것입니다.
그러나 스크립트를 실행할 때마다 다른 줄을 선택하고 싶습니다. 이를위한 해결책이 있습니까? 전체 스크립트를 원하지 않습니다. 그 단순한 것만.
답변:
shuf
유틸리티를 사용 하여 파일에서 임의의 줄을 인쇄 할 수 있습니다
$ shuf -n 1 filename
-n
: 인쇄 할 줄 수
예 :
$ shuf -n 1 /etc/passwd
git:x:998:998:git daemon user:/:/bin/bash
$ shuf -n 2 /etc/passwd
avahi:x:84:84:avahi:/:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
n
인쇄 할 줄 수 를 나타내지 않아도됩니다 . (즉, 한 줄 또는 두 줄만 원하는지 여부). 줄 번호가 아닙니다 (즉, 첫 번째 줄 두 번째 줄).
date +%S
)을 변수 x에 저장 한 다음 텍스트 파일 의 head
and tail
명령을 사용하여 x 번째 줄을 선택 하십시오. 어쨌든 방법이 더 쉽습니다. 감사합니다
shuf
은 coreutils에 있으므로 기본적으로 사용 가능합니다. 참고 : 입력 파일을 메모리에로드합니다. 이를 필요로하지 않는 효율적인 알고리즘이 있습니다 .
그냥 재미를 위해, 여기입니다 순수 bash는 솔루션을 사용하지 않는 shuf
, sort
, wc
, sed
, head
, tail
또는 기타 외부 도구를 제공합니다.
shuf
변형에 비해 유일한 장점 은 순수한 배쉬이기 때문에 약간 빠릅니다. 내 컴퓨터에서 1000 줄의 파일의 경우 shuf
변형은 약 0.1 초가 걸리고 다음 스크립트는 약 0.01 초가 걸립니다.) shuf
가장 쉽고 짧은 변형이지만 빠릅니다.
모든 정직에서 나는 shuf
고효율이 중요한 관심사가 아니라면 여전히 해결책을 원할 것 입니다.
#!/bin/bash
FILE=file.txt
# get line count for $FILE (simulate 'wc -l')
lc=0
while read -r line; do
((lc++))
done < $FILE
# get a random number between 1 and $lc
rnd=$RANDOM
let "rnd %= $lc"
((rnd++))
# traverse file and find line number $rnd
i=0
while read -r line; do
((i++))
[ $i -eq $rnd ] && break
done < $FILE
# output random line
printf '%s\n' "$line"
shuf
어쨌든 사용하는 것이 훨씬 좋습니다. 그것을 생각할 때, shuf
나는 이전에 쓴 것처럼 순수한 배쉬가 실제로 사용하는 것보다 효율적이라고 생각하지 않습니다 . 외부 도구를 실행할 때 가장 작은 (일정한) 오버 헤드가있을 수 있지만 해석 된 bash보다 더 빨리 실행됩니다. 그래서 shuf
확실히 더 나은 확장 할 수 있습니다. 스크립트가 교육적인 목적에 부합한다고 가정 해 봅시다. 그것이 가능하다는 것을 알게되어 기쁩니다.)
shuf
GNU Coreutils에 따라 다릅니다 (예 : FreeBSD 10.0에는 해당되지 않음). sort -R
이식성이 있지만 다른 (관련된) 문제를 해결합니다. 여러 줄로 표시되는 문자열은 한 번만 나타나는 확률과 같습니다. (물론, wc
다른 유틸리티도 여전히 사용할 수 있습니다.) 여기서 가장 큰 한계는 32768 번째 줄 이후에는 아무 것도 선택하지 않는다는 것입니다.
$((RANDOM<<15|RANDOM))
0..2 ^ 30-1입니다. @JFSebastian 보다 빈번한 입력으로 기울어지는 shuf
것은 아닙니다 sort -R
. 그 shuf -n 1
자리에 놓고 sort -R | head -n1
비교하십시오. 참조 (Btw는 10 ^ 3 반복은. 차이를 보여 매우 아직 충분히 빠른 10 ^ 6 인) 시각적 데모, 거친 과 어리 석음의이 비트는 모든 문자열 고주파있는 큰 입력에서 작동 보여 .
dieharder
모두 0 인 것 같습니다. 이것이 내 입장에서 이상한 실수가 아니라고 가정하면 왜 그것이 무작위가 아닌지 설명 할 것입니다! while echo $(( RANDOM << 17 | RANDOM << 2 | RANDOM >> 13 )); do :; done | perl -ne 'print pack "I>"' > out
잠시 동안 실행 한 다음 out
16 진 편집기 를 사용 하여 내용을 검사 하면 멋진 데이터를 얻 습니까? (또는 그러나 다른 사람처럼 볼 수 있습니다.) 나는 모든 0을 얻고, RANDOM
범인되지 않습니다 : 내가 교체 할 때 나는 모든 0을 얻을 $(( RANDOM << 17 | RANDOM << 2 | RANDOM >> 13 ))
과 100
너무.
파일이 있다고 가정 해보십시오 notifications.txt
. 랜덤 생성기의 범위를 결정하기 위해 총 라인 수를 계산해야합니다.
$ cat notifications.txt | wc -l
변수에 쓸 수 있습니다.
$ LINES=$(cat notifications.txt | wc -l)
이제부터 숫자를 생성 0
하기 $LINE
위해 RANDOM
변수 를 사용 합니다.
$ echo $[ $RANDOM % LINES]
변수에 쓸 수 있습니다.
$ R_LINE=$(($RANDOM % LINES))
이제이 줄 번호 만 인쇄하면됩니다 :
$ sed -n "${R_LINE}p" notifications.txt
RANDOM 소개 :
RANDOM Each time this parameter is referenced, a random integer between
0 and 32767 is generated. The sequence of random numbers may be
initialized by assigning a value to RANDOM. If RANDOM is unset,
it loses its special properties, even if it is subsequently
reset.
파일의 행 번호가 32767 미만인지 확인하십시오. 참조 이 당신이 상자 밖으로 작동 더 큰 무작위로 생성이 필요합니다.
예:
$ od -A n -t d -N 3 /dev/urandom | tr -d ' '
LINES=$(wc -l < file.txt); R_LINE=$((RANDOM % LINES)); sed -n "${R_LINE}p" file.txt
$RANDOM % n
분포 $RANDOM
가
입력 파일 또는 stdin에서 임의의 행을 선택하는 Python 스크립트는 다음과 같습니다.
#!/usr/bin/env python
"""Usage: select-random [<file>]..."""
import random
def select_random(iterable, default=None, random=random):
"""Select a random element from iterable.
Return default if iterable is empty.
If iterable is a sequence then random.choice() is used for efficiency instead.
If iterable is an iterator; it is exhausted.
O(n)-time, O(1)-space algorithm.
"""
try:
return random.choice(iterable) # O(1) time and space
except IndexError: # empty sequence
return default
except TypeError: # not a sequence
return select_random_it(iter(iterable), default, random.randrange)
def select_random_it(iterator, default=None, randrange=random.randrange):
"""Return a random element from iterator.
Return default if iterator is empty.
iterator is exhausted.
O(n)-time, O(1)-space algorithm.
"""
# from /programming//a/1456750/4279
# select 1st item with probability 100% (if input is one item, return it)
# select 2nd item with probability 50% (or 50% the selection stays the 1st)
# select 3rd item with probability 33.(3)%
# select nth item with probability 1/n
selection = default
for i, item in enumerate(iterator, start=1):
if randrange(i) == 0: # random [0..i)
selection = item
return selection
if __name__ == "__main__":
import fileinput
import sys
random_line = select_random_it(fileinput.input(), '\n')
sys.stdout.write(random_line)
if not random_line.endswith('\n'):
sys.stdout.write('\n') # always append newline at the end
알고리즘은 O (n) 시간, O (1) 공간입니다. 32767 라인보다 큰 파일에서 작동합니다. 입력 파일을 메모리에로드하지 않습니다. 각 입력 행을 정확히 한 번 읽습니다. 즉, 임의의 큰 (그러나 유한 한) 컨텐츠를 파이프에 파이프 할 수 있습니다. 다음 은 알고리즘에 대한 설명입니다 .
나는 Malte Skoruppa와 다른 사람들이 한 일에 깊은 인상을 받았지만, 여기에 그것을하는 훨씬 더 간단한 "순수한 배쉬"방법이 있습니다 :
IFS=$'\012'
# set field separator to newline only
lines=( $(<test5) )
# slurp entire file into an array
numlines=${#lines[@]}
# count the array elements
num=$(( $RANDOM$RANDOM$RANDOM % numlines ))
# get a (more-or-less) random number within the correct range
line=${lines[$num]}
# select the element corresponding to the random number
echo $line
# display it
일부 사람들이 언급했듯이 $ RANDOM은 무작위가 아닙니다. 그러나 필요에 따라 $ RANDOM을 함께 묶으면 32767 행의 파일 크기 제한을 극복 할 수 있습니다.