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

190

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

  • 0
    Каждая строка дополняется до фиксированной длины?
  • 0
    нет, каждая строка имеет переменное количество символов
Показать ещё 1 комментарий
Теги:
command-line

13 ответов

277

Вы можете использовать shuf:

shuf -n 1 $FILE

Существует также утилита rl. В Debian это в пакете randomize-lines, который делает именно то, что вы хотите, хотя и не доступен во всех дистрибутивах. На своей домашней странице он рекомендует использовать вместо shuf (чего не было, когда он был создан, я считаю). shuf является частью GNU coreutils, rl не является.

rl -c 1 $FILE
  • 0
    мне действительно нравится этот подход шуфа!
  • 2
    Спасибо за подсказку shuf , она встроена в Fedora.
Показать ещё 16 комментариев
63

Другая альтернатива:

head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1
  • 28
    $ {RANDOM} генерирует только числа меньше 32768, поэтому не используйте это для больших файлов (например, словарь английского языка).
  • 3
    Это не дает вам одинаковую вероятность для каждой строки из-за операции по модулю. Это едва имеет значение, если длина файла << 32768 (и совсем не так, если оно делит это число), но, возможно, стоит отметить.
Показать ещё 4 комментария
50
sort --random-sort $FILE | head -n 1

(Мне нравится, что подход shuf выше даже лучше - я даже не знал, что существует, и я бы никогда не нашел этот инструмент самостоятельно)

  • 10
    +1 Мне это нравится, но вам может потребоваться совсем недавняя sort , не работающая ни на одной из моих систем (CentOS 5.5, Mac OS 10.7.2). Кроме того, бесполезное использование cat может быть уменьшено до sort --random-sort < $FILE | head -n 1
  • 0
    sort -R <<< $'1\n1\n2' | head -1 скорее всего, вернет 1 и 2, потому что sort -R сортирует повторяющиеся строки вместе. То же самое относится и к sort -Ru , потому что он удаляет повторяющиеся строки.
Показать ещё 6 комментариев
11

perlfaq5: Как выбрать случайную строку из файла? Здесь используется алгоритм выборки коллектора из книги Camel:

$ perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file

Это имеет существенное преимущество в пространстве над чтением всего файла. Вы можете найти доказательство этого метода в статье "Компьютерное программирование", том 2, раздел 3.4.2, Дональд Э. Кнут.

  • 1
    Просто для целей включения (в случае, если указанный сайт отключается), вот код, на который указывал Tracker1: "cat filename | perl -e 'while (<>) {push (@ _, $ _);} print @ _ [рандов () * @ _]; ';»
  • 3
    Это бесполезное использование кошки. Вот небольшое изменение кода, найденного в perlfaq5 (и любезно предоставлено книгой Camel): perl -e 'srand; rand ($.) <1 && ($ line = $ _) while <>; выведите $ line; ' имя файла
Показать ещё 4 комментария
10

с помощью bash script:

#!/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}
  • 0
    Случайный может быть 0, Sed нуждается в 1 для первой строки. sed -n 0p возвращает ошибку.
  • 0
    ммм - как насчет $ 1 для "tmp.txt" и $ 2 для NUM?
Показать ещё 8 комментариев
9

Это просто.

cat file.txt | shuf -n 1

Конечно, это чуть медленнее, чем "shuf -n 1 file.txt" на нем.

  • 1
    Лучший ответ. Я не знал об этой команде. Обратите внимание, что -n 1 указывает 1 строку, и вы можете изменить ее на более чем 1. shuf можно использовать и для других целей; Я просто передал ps aux и grep чтобы случайно убить процессы, частично совпадающие с именем.
4

Одиночная строка bash:

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

Незначительная проблема: дублировать имя файла.

  • 2
    более легкая проблема. выполнение этого в / usr / share / dict / words приводит к появлению слов, начинающихся с «A». Играя с этим, у меня примерно 90% слов "А" и 10% слов "Б". Пока не начинается с цифр, которые составляют заголовок файла.
  • 0
    wc -l < test.txt позволяет избежать необходимости cut трубу.
3

Вот простой Python script, который выполнит задание:

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 (линии)): rand = random.randint (0, len (линии) ) -1) print lines.pop (rand),
  • 0
    Глупая система комментариев с дерьмовым форматированием. Разве форматирование в комментариях не работало когда-то давно?
Показать ещё 4 комментария
2

Другой способ: awk '

awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name
  • 2
    Это использует awk и bash ( $RANDOM - это bashism ). Вот чистый метод awk (mawk), использующий ту же логику, что и приведенный выше код perlfaq5 @ Tracker1: awk 'rand() * NR < 1 { line = $0 } END { print line }' file.name (вау, он еще короче чем код perl!)
  • 0
    Этот код должен прочитать файл ( wc ), чтобы получить счетчик строк, а затем снова должен прочитать (часть) файл ( awk ), чтобы получить содержимое заданного случайного номера строки. Ввод / вывод будет намного дороже, чем получение случайного числа. Мой код читает файл только один раз. Проблема с awk rand() состоит в том, что он затрачивается на основе секунд, поэтому вы получите дубликаты, если будете запускать их слишком быстро.
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

Вот что я обнаружил, так как моя Mac OS не использует все легкие ответы. Я использовал команду jot для генерации числа, поскольку решения переменной $RANDOM кажутся не очень случайными в моем тесте. При тестировании моего решения у меня было большое разброс в решениях, представленных на выходе.

  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
#!/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]}
  • 0
    Так как $ RANDOM генерирует числа меньше, чем количество слов в / usr / share / dict / words, которое имеет 235886 (в любом случае на моем Mac), я просто генерирую 6 отдельных случайных чисел между 0 и 9 и объединяю их в строку. Затем я проверяю, что число меньше 235886. Затем удаляю начальные нули, чтобы проиндексировать слова, которые я сохранил в массиве. Поскольку каждое слово является отдельной строкой, это можно легко использовать для любого файла, чтобы случайным образом выбрать строку.
0

Используя только vanilla sed и awk и без использования $RANDOM, простой, экономный по размеру и разумно быстрый "однострочный" для выбора одной строки псевдослучайно из файла с именем FILENAME выглядит следующим образом:

sed -n $(awk 'END {srand(); r=rand()*NR; if (r<NR) {sub(/\..*/,"",r); r++;}; print r}' FILENAME)p FILENAME

(Это работает, даже если FILENAME пуст, и в этом случае линия не испускается.)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню