Я хочу перемешать строки текстового файла случайным образом и создать новый файл. Файл может иметь несколько тысяч строк.
Как я могу это сделать с 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.090s
0.289s
0.589s
1.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.229s
bash
петли + 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 будет содержать рандомизированный список строк.
Надеюсь это поможет.
Пока не упоминается:
unsort
Util. Синтаксис (несколько ориентированный на плейлист):
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