Хочу випадково перетасувати рядки текстового файлу та створити новий файл. Файл може мати кілька тисяч рядків.
Як я можу це зробити з cat
, awk
, cut
і т.д.?
Хочу випадково перетасувати рядки текстового файлу та створити новий файл. Файл може мати кілька тисяч рядків.
Як я можу це зробити з cat
, awk
, cut
і т.д.?
Відповіді:
Можна використовувати shuf
. Принаймні, у деяких системах (схоже, це не в POSIX).
Як зазначав jleedev: 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
адаптована з власної відповіді ОП :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 року з 3,2 ГГц Intel Core i5 та Fusion Drive під управлінням 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
петлі та Скали.Версії Windows для рішення Python (код 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, який я називаю "несоромним":
#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
У мене також є версія, обмежена NULL, яка називається "unsort0" ... зручна для використання з find -print0 тощо.
PS: Я також проголосував за "шуф", я не мав уявлення про те, що це було в 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's 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 -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
Але дивіться цю публікацію щодо недоліків пітона 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
вихідний формат, тому що, використовуючи стандартний формат %.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 , але а) приймає 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
Це сценарій 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 має мінімальну залежність (лише сортування та 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
, але продуктивність буде проблемою з більшим вкладом; правильне використання єдиного $RANDOM
значення перетасовується лише до 32 768 вхідних рядків; хоча ви можете розширити цей діапазон, напевно, це не варто: наприклад, на моїй машині запуск сценарію на 32 768 коротких рядків введення займає близько 1 секунди, що приблизно в 150 разів більше, ніж shuf
займає біг , і приблизно в 10-15 разів до тих пір, поки awk
потрібне рішення власного сприяння ОП . Якщо ви можете розраховувати на sort
присутність, awk
має бути і там.
У Windows Ви можете спробувати цей пакетний файл, який допоможе перетасувати дані.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