Я хочу случайным образом перемешать строки текстового файла и создать новый файл. Файл может содержать несколько тысяч строк.
Как я могу сделать это с cat
, awk
, cut
и т.д.?
Я хочу случайным образом перемешать строки текстового файла и создать новый файл. Файл может содержать несколько тысяч строк.
Как я могу сделать это с cat
, awk
, cut
и т.д.?
Вы можете использовать shuf
. На некоторых системах, по крайней мере (не похоже на POSIX).
Как отметил Джеледев: sort -R
также может быть вариантом. По крайней мере на некоторых системах; ну, вы получите картину. Было указано, что sort -R
не перетасовывает, а сортирует элементы в соответствии с их хэш-значением.
[Примечание редактора: sort -R
почти перетасовки, за исключением того, что повторяющиеся строки/ключи сортировки всегда заканчиваются рядом друг с другом. Другими словами: только с уникальными входными строками/клавишами это истинный перетасовка. Хотя верно, что порядок вывода определяется хэш-значениями, случайность возникает из выбора случайной хэш-функции - см. manual.]
Perl one-liner будет простой версией решения Maxim
perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
Этот ответ дополняет многие великие существующие ответы следующими способами:
Существующие ответы упакованы в функции гибкой оболочки:
stdin
, но, в качестве альтернативы, также аргументы имени файлаSIGPIPE
обычным способом (тихое завершение с кодом выхода 141
), в отличие от шумного взлома. Это важно при передаче вывода функции в канал, который закрыт раньше, например, при передаче в head
.Сравнение производительности.
awk
, sort
и cut
, адаптированная из собственного ответа OP:shuf() { awk 'BEGIN {srand(); OFMT="%.17f"} {print rand(), $0}' "[email protected]" |
sort -k1,1n | cut -d ' ' -f2-; }
shuf() { perl -MList::Util=shuffle -e 'print shuffle(<>);' "[email protected]"; }
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))
' "[email protected]"; }
See the bottom section for a Windows version of this function.
shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
puts ARGF.readlines.shuffle' "[email protected]"; }
Сравнение производительности:
Примечание. Эти цифры были получены на iMac, выпущенном в конце 2012 года, с процессором Intel Core i5 с тактовой частотой 3,2 ГГц и диском Fusion, работающим под управлением OSX 10.10.3. Хотя время зависит от используемой ОС, технических характеристик машины, используемой реализации awk
(например, версия BSD awk
, используемая в 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
loop + sort
32.593s
Выводы:
shuf
, если можете - это самый быстрый на сегодняшний день.awk
+ sort
+ cut
в качестве крайней меры; какая реализация awk
вы используете (имеет значение mawk
быстрее, чем GNU awk
, BSD awk
медленнее).sort -R
, bash
и Scala.Версии решения 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))" %*
Я использую крошечный perl script, который я называю "unsort":
#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
У меня также есть NULL-разделительная версия, называемая "unsort0"... удобная для использования с find -print0 и т.д.
PS: Проголосовал за "shuf" тоже, я понятия не имел, что было в Coreutils в эти дни... вышеупомянутое может по-прежнему быть полезным, если ваши системы не имеют "shuf".
Вот первая попытка, которая проста в кодере, но сложна на процессоре, который добавляет случайное число к каждой строке, сортирует их, а затем удаляет случайное число из каждой строки. По сути, строки сортируются случайным образом:
cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled
здесь awk script
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
Один слой для 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) элементами.
Простая функция на основе awk выполнит задание:
shuffle() {
awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8-
}
использование:
any_command | shuffle
Это должно работать практически в любой UNIX. Протестировано на Linux, Solaris и HP-UX.
Update:
Обратите внимание, что ведущие нули (%06d
) и rand()
умножают его на правильную работу и на системы, где sort
не понимает числа. Его можно сортировать по лексикографическому порядку (сравнение нормальных строк a.k.a.).
Ruby FTW:
ls | ruby -e 'puts STDIN.readlines.shuffle'
Один лайнер для Python на основе scai answer, но a) принимает stdin, b) делает результат повторяемым с семенем, 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
У нас есть пакет для выполнения самой работы:
sudo apt-get install randomize-lines
Пример:
Создайте упорядоченный список чисел и сохраните его до 1000.txt:
seq 1000 > 1000.txt
чтобы перетасовать его, просто используйте
rl 1000.txt
Это python script, который я сохранил как 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
Если у вас установлен Scala, здесь один-лайнер для перетасовки ввода:
ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
Эта функция bash имеет минимальную зависимость (только сортировка и bash):
shuf() {
while read -r x;do
echo $RANDOM$'\x1f'$x
done | sort |
while IFS=$'\x1f' read -r x y;do
echo $y
done
}
В окнах Вы можете попробовать этот командный файл, чтобы помочь вам перетасовать файл 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
Другой вариант awk
:
#!/usr/bin/awk -f
# usage:
# awk -f randomize_lines.awk lines.txt
# usage after "chmod +x randomize_lines.awk":
# randomize_lines.awk lines.txt
BEGIN {
FS = "\n";
srand();
}
{
lines[ rand()] = $0;
}
END {
for( k in lines ){
print lines[k];
}
}