Хочу випадково перетасувати рядки текстового файлу та створити новий файл. Файл може мати кілька тисяч рядків.
Як я можу це зробити з 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.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петлі та Скали.Версії 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 буде містити рандомізований список рядків.
Сподіваюсь, це допомагає.
Ще не згадано:
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