Какой простой способ прочитать случайную строку из файла в командной строке Unix?
Какой простой способ прочитать случайную строку из файла в командной строке Unix?
Ответы:
Вы можете использовать shuf
:
shuf -n 1 $FILE
Также есть утилита под названием rl
. В Debian он входит в randomize-lines
пакет, который делает именно то, что вы хотите, хотя и не доступен во всех дистрибутивах. На своей домашней странице он фактически рекомендует использовать shuf
вместо этого (который, я думаю, не существовал, когда он был создан). shuf
является частью GNU coreutils, rl
не является.
rl -c 1 $FILE
shuf
совет, он встроен в Fedora.
sort -R
определенно собирается сделать один ждать много , если дело со значительно большими файлами - 80kk линии -, в то время как, shuf -n
действует совершенно мгновенно.
coreutils
из Homebrew. Может быть вызван gshuf
вместо shuf
.
randomize-lines
на OS Xbrew install randomize-lines; rl -c 1 $FILE
shuf
является частью GNU Coreutils и поэтому не обязательно будет доступен (по умолчанию) в системах * BSD (или Mac?). Перл @ Tracker1 с одной строкой ниже более переносим (и, по моим тестам, немного быстрее).
Другая альтернатива:
head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1
(${RANDOM} << 15) + ${RANDOM}
. Это значительно уменьшает смещение и позволяет работать с файлами, содержащими до 1 миллиарда строк.
+
и |
являются одинаковыми, так как ${RANDOM}
это 0..32767 по определению.
sort --random-sort $FILE | head -n 1
(Мне нравится вышеописанный подход к шуфу, хотя я даже не знал, что он существует, и я бы никогда не нашел этот инструмент самостоятельно)
sort
, он не работал ни на одной из моих систем (CentOS 5.5, Mac OS 10.7.2). Кроме того, бесполезное использование кошки может быть сокращено доsort --random-sort < $FILE | head -n 1
sort -R <<< $'1\n1\n2' | head -1
с большой вероятностью вернет 1 и 2, потому что sort -R
сортирует повторяющиеся строки вместе. То же самое относится и к sort -Ru
, потому что он удаляет дубликаты строк.
sort
перед передачей head
. shuf
вместо этого выбирает случайные строки из файла и для меня это намного быстрее.
sort --random-sort $FILE | head
бы @SteveKehlet , поскольку это позволяет ему напрямую обращаться к файлу, возможно, обеспечивая эффективную параллельную сортировку
--random-sort
И -R
опции являются специфическими для GNU рода (так что они не будут работать с BSD или Mac OS sort
). GNU sort узнал эти флаги в 2005 году, поэтому вам нужен GNU coreutils 6.0 или новее (например, CentOS 6).
Это просто
cat file.txt | shuf -n 1
Конечно, это немного медленнее, чем сам по себе "shuf -n 1 file.txt".
-n 1
указывается 1 строка, и вы можете изменить ее на более чем 1. shuf
Можно использовать и для других вещей; Я просто отправил ps aux
и grep
с его помощью случайным образом убить процессы, частично соответствующие имени.
perlfaq5: как выбрать случайную строку из файла? Вот алгоритм отбора проб из книги верблюдов:
perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file
Это имеет значительное преимущество в пространстве перед чтением всего файла. Доказательство этого метода можно найти в книге «Искусство компьютерного программирования», том 2, раздел 3.4.2, Дональда Кнута.
shuf
. Код на Perl немного быстрее (на 8% быстрее, на время пользователя, на 24% - на системное время), хотя я обнаружил, что код perl «кажется» менее случайным (я написал музыкальный автомат с его использованием).
shuf
хранит весь входной файл в памяти , что является ужасной идеей, в то время как этот код хранит только одну строку, поэтому пределом этого кода является количество строк INT_MAX (2 ^ 31 или 2 ^ 63 в зависимости от вашего арка), предполагая, что любая из его выбранных потенциальных линий помещается в память.
используя скрипт 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}
Одиночная линия bash:
sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt
Небольшая проблема: дублирование имени файла.
wc -l < test.txt
избегает необходимости трубить к cut
.
Вот простой скрипт 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
import random, sys lines = open(sys.argv[1]).readlines()
для i в диапазоне (len (линии)): rand = random.randint (0, len (lines) -1) print lines.pop (rand),
len(lines)
может привести к IndexError. Вы могли бы использовать print(random.choice(list(open(sys.argv[1]))))
. Существует также эффективный для памяти алгоритм отбора проб из пласта .
Еще один способ использования « awk »
awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name
wc
), чтобы получить счетчик строк, а затем снова должен прочитать (часть) файл ( awk
), чтобы получить содержимое заданного случайного номера строки. Ввод / вывод будет намного дороже, чем получение случайного числа. Мой код читает файл только один раз. Проблема с awk в rand()
том, что он начинается с секунд, поэтому вы получите дубликаты, если будете запускать их слишком быстро.
Решение, которое также работает на 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
в предыдущем примере.#!/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]}
Вот что я обнаружил, так как моя 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
Отголосок переменной - получить визуальное представление о сгенерированном случайном числе.
Использование только 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 пусто, и в этом случае строка не выводится.)
Одним из возможных преимуществ этого подхода является то, что он вызывает rand () только один раз.
Как отметил @AdamKatz в комментариях, другой возможностью было бы вызвать rand () для каждой строки:
awk 'rand() * NR < 1 { line = $0 } END { print line }' FILENAME
(Простое доказательство правильности может быть дано на основе индукции.)
rand()
«В большинстве реализаций awk, включая gawk, rand () начинает генерировать числа из одного и того же начального номера или seed при каждом запуске awk».
- https://www.gnu.org/software/gawk/manual/html_node/Numeric-Functions.html