Я хочу перемешать строки текстового файла случайным образом и создать новый файл. Файл может иметь несколько тысяч строк.
Как я могу это сделать с cat, awk, cutи т.д.?
Я хочу перемешать строки текстового файла случайным образом и создать новый файл. Файл может иметь несколько тысяч строк.
Как я могу это сделать с cat, awk, cutи т.д.?
Ответы:
Вы можете использовать shuf. По крайней мере, в некоторых системах (похоже, не в POSIX).
Как указал Джлеедев, sort -Rэто тоже может быть вариантом. По крайней мере, в некоторых системах; ну, вы поняли. Было отмечено, что на sort -Rсамом деле это не случайный случай, а сортировка элементов по их хэш-значению.
[Примечание редактора: sort -R почти тасуется, за исключением того, что дублирующие строки / ключи сортировки всегда оказываются рядом друг с другом . Другими словами: только с уникальными входными строками / клавишами это настоящая случайность. Хотя порядок вывода определяется значениями хеш-функции , случайность определяется выбором случайной хэш- функции - см. Руководство .]
shufи sort -Rнемного отличаются, потому что sort -Rслучайным образом упорядочивает элементы в соответствии с их хешем , то есть sort -Rобъединяет повторяющиеся элементы, в то время как shufвсе элементы перемешиваются случайным образом.
brew install coreutilsзатем используйте gshuf ...(:
sort -Rи shufдолжен рассматриваться как совершенно другой. sort -Rявляется детерминированным. Если вы вызываете его дважды в разное время на одном и том же входе, вы получите один и тот же ответ. shufс другой стороны, производит случайный вывод, так что он, скорее всего, будет давать разные выходные данные на одном и том же входе.
Perl one-liner будет простой версией решения Максима
perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
\n; да, это \nдолжно присутствовать - и это , как правило , это - в противном случае вы получите то , что вы описываете.
<STDIN>на <>, так что решение работает и с вводом из файлов .
Этот ответ дополняет многие великие существующие ответы следующими способами:
Существующие ответы упакованы в гибкие функции оболочки :
stdinввод, но также и аргументы имени файлаSIGPIPEобычным способом (тихое завершение с кодом выхода 141), в отличие от шумного взлома. Это важно при передаче вывода функции в трубу, которая закрывается раньше, например, при передаче в head.Сравнение производительности производится.
awk, sortиcut , адаптировано из собственного ответа OP в :shuf() { awk 'BEGIN {srand(); OFMT="%.17f"} {print rand(), $0}' "$@" |
sort -k1,1n | cut -d ' ' -f2-; }
shuf() { perl -MList::Util=shuffle -e 'print shuffle(<>);' "$@"; }
shuf() { python -c '
import sys, random, fileinput; from signal import signal, SIGPIPE, SIG_DFL;
signal(SIGPIPE, SIG_DFL); lines=[line for line in fileinput.input()];
random.shuffle(lines); sys.stdout.write("".join(lines))
' "$@"; }
См. Нижний раздел для версии Windows для этой функции.
shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
puts ARGF.readlines.shuffle' "$@"; }
Сравнение производительности:
Примечание. Эти цифры были получены на iMac, выпущенном в конце 2012 года, с процессором Intel Core i5 с тактовой частотой 3,2 ГГц и диском Fusion, работающим под управлением OSX 10.10.3. Хотя время будет зависеть от используемой ОС, технических характеристик машины, awkиспользуемой реализации (например, awkверсия BSD, используемая в OSX, обычно медленнее, чем GNU awkи особенно mawk), это должно дать общее представление об относительной производительности .
Входной файл представляет собой файл с 1 миллионом строк, созданный с помощью seq -f 'line %.0f' 1000000.
Время указано в порядке возрастания (сначала самое быстрое):
shuf
0.090s0.289s0.589s1.342sс Python 2.7.6; 2.407s(!) с Python 3.4.2awk+ sort+cut
3.003sс BSD awk; 2.388sс GNU awk(4.1.1); 1.811sс mawk(1.3.4);Для дальнейшего сравнения решения не упакованы как функции выше:
sort -R (не настоящий случай, если есть повторяющиеся строки ввода)
10.661s - выделение большего количества памяти, кажется, не имеет значения24.229sbash петли + sort
32.593sВыводы :
shuf, если можете - это самый быстрый на сегодняшний день.awk+ sort+ cutкомбо в крайнем случае ; какая awkреализация вы используете, имеет значение ( mawkбыстрее, чем GNU awk, BSD awkмедленнее).sort -R, bashпетли и Скала.Версии решения Python для Windows (код Python идентичен, за исключением различий в кавычках и удалении операторов, связанных с сигналами, которые не поддерживаются в Windows):
$OutputEncodingесли вы хотите отправлять не-ASCII символы через конвейер):# Call as `shuf someFile.txt` or `Get-Content someFile.txt | shuf`
function shuf {
$Input | python -c @'
import sys, random, fileinput;
lines=[line for line in fileinput.input()];
random.shuffle(lines); sys.stdout.write(''.join(lines))
'@ $args
}
Обратите внимание, что PowerShell может напрямую перетасовываться с помощью своего Get-Randomкомандлета (хотя производительность может быть проблемой); например:
Get-Content someFile.txt | Get-Random -Count ([int]::MaxValue)
cmd.exe(пакетный файл):Сохранить в файл shuf.cmd, например:
@echo off
python -c "import sys, random, fileinput; lines=[line for line in fileinput.input()]; random.shuffle(lines); sys.stdout.write(''.join(lines))" %*
python -c "import sys, random; lines = [x for x in sys.stdin.read().splitlines()] ; random.shuffle(lines); print(\"\n\".join([line for line in lines]));"
from signal import signal, SIGPIPE, SIG_DFL; signal(SIGPIPE, SIG_DFL);исходное решение и сохранить гибкость, позволяя также передавать аргументы имени файла - не нужно ничего менять (кроме цитирования) - смотрите новый раздел, который я добавил на низ.
Я использую крошечный Perl-скрипт, который я называю «unsort»:
#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
У меня также есть версия с разделением NULL, которая называется "unsort0" ... удобная для использования с find -print0 и так далее.
PS: проголосовал также за 'shuf', я понятия не имел, что было в coreutils в эти дни ... вышеупомянутое может все еще быть полезным, если в ваших системах нет 'shuf'.
<STDIN>с <>тем чтобы сделать работу решения с вводом из файлов тоже.
Вот первая попытка, которая проста для кодера, но тяжела для процессора, который добавляет случайное число к каждой строке, сортирует их и затем удаляет случайное число из каждой строки. По сути, строки сортируются случайным образом:
cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled
head myfile | awk .... Тогда я просто изменяю это на кошку; вот почему он был оставлен там.
-k1 -nдля сортировки, поскольку выходные данные awk rand()- это десятичные числа от 0 до 1, и потому, что все, что имеет значение, это то, что они каким-то образом переупорядочиваются. -k1может помочь ускорить его, игнорируя оставшуюся часть строки, хотя вывод rand () должен быть достаточно уникальным, чтобы замкнуть сравнение.
cat filename |(или < filename |), чем помнить, как каждая отдельная программа принимает входной файл (или нет).
вот сценарий awk
awk 'BEGIN{srand() }
{ lines[++d]=$0 }
END{
while (1){
if (e==d) {break}
RANDOM = int(1 + rand() * d)
if ( RANDOM in lines ){
print lines[RANDOM]
delete lines[RANDOM]
++e
}
}
}' file
вывод
$ cat file
1
2
3
4
5
6
7
8
9
10
$ ./shell.sh
7
5
10
9
6
8
2
1
3
4
awkс sortи cut. Для не более нескольких тысяч строк это не имеет большого значения, но при большем количестве строк это имеет значение (порог зависит от используемой awkреализации). Небольшое упрощение было бы заменить строки while (1){и if (e==d) {break}с while (e<d).
Однострочник для Python:
python -c "import random, sys; lines = open(sys.argv[1]).readlines(); random.shuffle(lines); print ''.join(lines)," myFile
А для печати всего одна случайная строка:
python -c "import random, sys; print random.choice(open(sys.argv[1]).readlines())," myFile
Но посмотрите этот пост на наличие недостатков Python random.shuffle(). Это не будет хорошо работать со многими (более 2080) элементами.
/dev/urandomделается. Для того, чтобы использовать его в Python: random.SystemRandom().shuffle(L).
.readLines()возвращает строки с завершающим переводом строки.
Простая функция на основе awk сделает эту работу:
shuffle() {
awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8-
}
использование:
any_command | shuffle
Это должно работать практически на любой UNIX. Протестировано на Linux, Solaris и HP-UX.
Обновить:
Обратите внимание, что ведущие нули ( %06d) и rand()умножение заставляют его работать должным образом и в системах, где sortне понимают числа. Это может быть отсортировано по лексикографическому порядку (иначе как сравнение строк).
"$@", он также будет работать с файлами в качестве входных данных. Нет смысла умножать rand(), потому что sort -nспособен сортировать десятичные дроби. Это, однако, хорошая идея управления awkформатом вывода «s, потому что с форматом по умолчанию, в %.6g, rand()будет выводить случайные числа в экспоненциальной нотации. Хотя на практике, возможно, достаточно перетасовать до 1 миллиона строк, легко поддерживать большее количество строк без значительных потерь производительности; например %.17f.
sortдолжен обрабатывать десятичные дроби (даже с тысячами разделителей, как я только что заметил).
Ruby FTW:
ls | ruby -e 'puts STDIN.readlines.shuffle'
puts ARGF.readlines.shuffle, вы можете заставить его работать как с вводом stdin, так и с аргументами имени файла.
ruby -e 'puts $<.sort_by{rand}'- ARGF уже перечислим, поэтому мы можем перетасовать строки, сортируя их по случайным значениям.
Один лайнер для Python, основанный на ответе scai , но a) принимает stdin, b) делает результат повторяемым с помощью seed, c) выбирает только 200 из всех строк.
$ cat file | python -c "import random, sys;
random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \
> 200lines.txt
Простой и интуитивно понятный способ будет использовать shuf.
Пример:
Предположим words.txtкак:
the
an
linux
ubuntu
life
good
breeze
Чтобы перемешать строки, выполните:
$ shuf words.txt
который бросил бы перемешанные строки к стандартному выводу ; Итак, вы должны передать его в выходной файл, например:
$ shuf words.txt > shuffled_words.txt
Один такой случайный ход может дать:
breeze
the
linux
an
ubuntu
good
life
Это скрипт на python, который я сохранил как rand.py в моей домашней папке:
#!/bin/python
import sys
import random
if __name__ == '__main__':
with open(sys.argv[1], 'r') as f:
flist = f.readlines()
random.shuffle(flist)
for line in flist:
print line.strip()
На Mac OSX sort -Rи shufне доступны, поэтому вы можете использовать псевдоним этого в вашем bash_profile как:
alias shuf='python rand.py'
Если, как и я, вы пришли сюда, чтобы найти альтернативу shufдля macOS, используйте randomize-lines.
Установить randomize-lines(homebrew) пакет, в котором есть rlкоманда, аналогичная функциональности shuf.
brew install randomize-lines
Usage: rl [OPTION]... [FILE]...
Randomize the lines of a file (or stdin).
-c, --count=N select N lines from the file
-r, --reselect lines may be selected multiple times
-o, --output=FILE
send output to file
-d, --delimiter=DELIM
specify line delimiter (one character)
-0, --null set line delimiter to null character
(useful with find -print0)
-n, --line-number
print line number with output lines
-q, --quiet, --silent
do not output any errors or warnings
-h, --help display this help and exit
-V, --version output version information and exit
brew install coreutilsобеспечивает shufбинарный как gshuf.
Если у вас установлен Scala, вот одна строка для перетасовки ввода:
ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
Эта функция bash имеет минимальную зависимость (только sort и bash):
shuf() {
while read -r x;do
echo $RANDOM$'\x1f'$x
done | sort |
while IFS=$'\x1f' read -r x y;do
echo $y
done
}
awkрешению OP, но производительность будет проблемой при большем вводе; использование вами одного $RANDOMзначения корректно перетасовывает только до 32 768 строк ввода; хотя вы могли бы расширить этот диапазон, это, вероятно, того не стоит: например, на моем компьютере выполнение сценария на 32 768 коротких строках ввода занимает около 1 секунды, что примерно в 150 раз больше времени выполнения shuf, и примерно в 10–15 раз. столько, сколько awkтребуется собственному решению ОП. Если вы можете положиться на sortприсутствие, awkдолжно быть там.
В Windows Вы можете попробовать этот пакетный файл, чтобы помочь вам перетасовать ваш data.txt. Использование пакетного кода
C:\> type list.txt | shuffle.bat > maclist_temp.txt
После выполнения этой команды maclist_temp.txt будет содержать рандомизированный список строк.
Надеюсь это поможет.
Пока не упоминается:
unsortUtil. Синтаксис (несколько ориентированный на плейлист):
unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic]
[--identity] [--filenames[=profile]] [--separator sep] [--concatenate]
[--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null]
[--linefeed] [file ...]msort может перетасовать строку, но обычно это перебор:
seq 10 | msort -jq -b -l -n 1 -c r