我正在尝试编写一个shell脚本。这个想法是从文本文件中随机选择一行,并将其显示为Ubuntu桌面通知。
但是我希望每次执行脚本时都选择不同的行。有解决方案吗?我不要整个剧本。只是那简单的事情而已。
我正在尝试编写一个shell脚本。这个想法是从文本文件中随机选择一行,并将其显示为Ubuntu桌面通知。
但是我希望每次执行脚本时都选择不同的行。有解决方案吗?我不要整个剧本。只是那简单的事情而已。
Answers:
您可以使用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,然后选择使用的是第x线head
和tail
命令从文本文件中。无论如何,您的方法更容易。谢谢
shuf
在coreutils中,因此默认情况下可用。注意:它将输入文件加载到内存中。有一种不需要的有效算法。
只是为了好玩,这里是一个纯bash的解决方案不使用shuf
,sort
,wc
,sed
,head
,tail
或任何其他外部工具。
相对于shuf
变体的唯一优点是它速度更快,因为它是纯bash。在我的机器上,对于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
无论如何,使用会更好。考虑到这一点,我不相信纯粹的bash实际上比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
和比较。(顺便说一下,10 ^ 3迭代比10 ^ 6更快,但仍然足以显示出差异。)另请参见更粗略,更直观的演示,这有点愚蠢,表明它适用于所有字符串都是高频的大型输入。
dieharder
似乎全为零。假设这不仅仅是我个人的一个奇怪错误,那肯定可以解释为什么它不是随机的!如果您运行while echo $(( RANDOM << 17 | RANDOM << 2 | RANDOM >> 13 )); do :; done | perl -ne 'print pack "I>"' > out
了一段时间,然后out
使用十六进制编辑器检查的内容,是否能获得美观的数据?(或者查看其他但是你喜欢。)我得到的所有零,而RANDOM
不是罪魁祸首:我得到的所有零,当我更换$(( 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
这是一个Python脚本,可从输入文件或stdin中选择一条随机行:
#!/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行的文件大小限制。