Який простий спосіб зчитувати випадковий рядок з файлу в командному рядку 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 X відbrew install randomize-lines; rl -c 1 $FILE
shuf
є частиною GNU Coreutils і тому не обов'язково буде доступна (за замовчуванням) у * BSD системах (або Mac?). @ Однолінійний Perl-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
було б найкраще, оскільки це дозволяє йому безпосередньо отримувати доступ до файлу, можливо, дозволяючи ефективно паралельне сортування
--random-sort
та -R
параметри характерні для сортування GNU (тому вони не працюватимуть із BSD чи Mac OS sort
). Сортування GNU вивчило ці прапори у 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}
Одинарна лінія баш:
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 in range (len (рядки)): rand = random.randint (0, len (рядки) -1) друкується 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's 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 не використовує всіх простих відповідей. Я використовував команду 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
Відлуння змінної полягає в тому, щоб отримати візуальне згенероване випадкове число.
Використовуючи тільки sed і awk vanilla та без використання $ 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()
"У більшості дивних реалізацій, включаючи gawk, rand () починає генерувати номери з одного і того ж початкового номера або насіння, щоразу запускаючи awk."
- https://www.gnu.org/software/gawk/manual/html_node/Numeric-Functions.html