Как отобразить случайную строку из текстового файла?

17

Я пытаюсь написать сценарий оболочки. Идея состоит в том, чтобы выбрать одну строку в случайном порядке из текстового файла и отобразить ее как оповещение рабочего стола Ubuntu.

Но я хочу, чтобы разные строки выбирались каждый раз при выполнении сценария. Есть ли решение для этого? Мне не нужен весь скрипт. Только эта простая вещь.

    
задан Anandu M Das 18.09.2014 в 13:12
источник

6 ответов

27

Вы можете использовать утилиту 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
    
ответ дан aneeshep 18.09.2014 в 13:20
источник
13

Вы также можете использовать команду sort для получения случайной строки из файла.

sort -R filename | head -n1
    
ответ дан g_p 18.09.2014 в 13:26
8

Просто для удовольствия, вот pure bash solution , который не использует 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"
    
ответ дан Malte Skoruppa 18.09.2014 в 14:15
3

Скажем, у вас есть файл 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 ' '
    
ответ дан c0rp 18.09.2014 в 14:01
2

Вот сценарий 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 https://stackoverflow.com/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) -time, O (1) -пространство. Он работает для файлов размером более 32767 строк. Он не загружает входные файлы в память. Он считывает каждую строку ввода ровно один раз, т. Е. Вы можете передать в нее произвольное большое (но конечное) содержимое. Ниже приведено описание объяснения алгоритма .

    
ответ дан jfs 24.09.2014 в 06:49
0

Меня впечатляет работа, которую Malte Skoruppa и другие сделали, но здесь намного проще использовать «чистый bash»:

IFS=$'2'
# 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 не является случайным. Тем не менее, ограничение размера файла 32767 строк преодолевается путем наложения $ RANDOM вместе по мере необходимости.     

ответ дан Wastrel 22.01.2018 в 19:05