Chcę losowo przetasować wiersze pliku tekstowego i utworzyć nowy plik. Plik może zawierać kilka tysięcy linii.
Jak mogę to zrobić z cat
, awk
, cut
itp?
Chcę losowo przetasować wiersze pliku tekstowego i utworzyć nowy plik. Plik może zawierać kilka tysięcy linii.
Jak mogę to zrobić z cat
, awk
, cut
itp?
Odpowiedzi:
Możesz użyć shuf
. Przynajmniej w niektórych systemach (wydaje się, że nie jest w POSIX).
Jak zauważył Jleedev: sort -R
może być również opcją. Przynajmniej na niektórych systemach; Cóż, rozumiesz, o co chodzi. Wskazano, że sort -R
tak naprawdę nie tasuje, ale sortuje elementy według ich wartości skrótu.
[Nota redaktora: sort -R
prawie tasuje, z tym że zduplikowane linie / klucze sortowania zawsze kończą się obok siebie . Innymi słowy: tylko przy unikalnych liniach / klawiszach wprowadzania jest to prawdziwy los. Chociaż prawdą jest, że kolejność wyjściowa zależy od wartości skrótu , losowość wynika z wyboru losowej funkcji skrótu - patrz instrukcja .]
shuf
i sort -R
różnią się nieznacznie, ponieważ sort -R
losowo porządkuje elementy zgodnie z ich skrótem , to znaczy sort -R
łączy powtarzające się elementy razem, a shuf
losowo tasuje wszystkie elementy.
brew install coreutils
gshuf ...
sort -R
i shuf
powinny być postrzegane jako zupełnie inne.sort -R
jest deterministyczny. Jeśli zadzwonisz dwukrotnie w różnych momentach na tym samym wejściu, otrzymasz tę samą odpowiedź. shuf
, z drugiej strony, generuje losowy wynik, więc najprawdopodobniej da inny wynik na tym samym wejściu.
One-liner Perla byłby prostą wersją rozwiązania Maxima
perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
\n
; tak, to \n
musi być obecne - i zwykle tak jest - w przeciwnym razie dostaniesz to, co opisujesz.
<STDIN>
z <>
, więc rozwiązanie działa z wejściem z plików też.
Ta odpowiedź uzupełnia wiele wspaniałych istniejących odpowiedzi na następujące sposoby:
Istniejące odpowiedzi są spakowane w elastyczne funkcje powłoki :
stdin
dane wejściowe, ale alternatywnie także argumenty nazwy plikuSIGPIPE
w zwykły sposób (ciche zakończenie z kodem wyjścia 141
), a nie głośne łamanie. Jest to ważne, gdy przesyłane jest wyjście funkcji do potoku, który jest wcześniej zamknięty, na przykład podczas przesyłania do head
.Dokonano porównania wydajności .
awk
, sort
orazcut
, zaadaptowany z własnej odpowiedzi 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))
' "$@"; }
W dolnej części znajduje się wersja tej funkcji dla systemu Windows .
shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
puts ARGF.readlines.shuffle' "$@"; }
Porównanie wydajności:
Uwaga: Liczby te uzyskano na komputerze iMac z końca 2012 roku z procesorem Intel Core i5 3,2 GHz i dyskiem Fusion z systemem OSX 10.10.3. Chociaż czasy różnią się w zależności od używanego systemu operacyjnego, specyfikacji komputera i awk
stosowanej implementacji (np. awk
Wersja BSD używana w OSX jest zwykle wolniejsza niż GNU, awk
a zwłaszcza mawk
), powinno to zapewnić ogólne poczucie względnej wydajności .
Wejście plik jest plik 1 miliona linii produkowane seq -f 'line %.0f' 1000000
.
Czasy są wymienione w kolejności rosnącej (najpierw najszybsze):
shuf
0.090s
0.289s
0.589s
1.342s
z Python 2.7.6; 2.407s
(!) z Python 3.4.2awk
+ sort
+cut
3.003s
z BSD awk
; 2.388s
z GNU awk
(4.1.1); 1.811s
z mawk
(1.3.4);W celu dalszego porównania rozwiązania niepakowane jako funkcje powyżej:
sort -R
(nie jest to prawdziwe losowanie, jeśli istnieją zduplikowane linie wejściowe)
10.661s
- przydzielanie większej ilości pamięci nie wydaje się mieć znaczenia24.229s
bash
pętle + sort
32.593s
Wnioski :
shuf
, jeśli możesz - zdecydowanie najszybszy.awk
+ sort
+ zgodnego z POSIX- cut
em w ostateczności ; której awk
implementacji używasz ma znaczenie ( mawk
jest szybszy niż GNU awk
, BSD awk
jest najwolniejszy).sort -R
, bash
pętle i Scala.Okna wersje Pythona roztworu (kod Python jest identyczne, z wyjątkiem zmian w cytowaniu i usuwania sprawozdania związane sygnału, które nie są obsługiwane w systemie Windows):
$OutputEncoding
jeśli chcesz wysyłać znaki inne niż ASCII przez potok):# 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
}
Zauważ, że PowerShell może natywnie tasować za pomocą polecenia Get-Random
cmdlet (choć wydajność może być problemem); na przykład:
Get-Content someFile.txt | Get-Random -Count ([int]::MaxValue)
cmd.exe
(plik wsadowy):Zapisz do pliku shuf.cmd
, na przykład:
@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);
oryginalnego rozwiązania jest wystarczające i zachowuje elastyczność umożliwiającą przekazywanie argumentów nazw plików - nie trzeba nic zmieniać (oprócz cytowania) - zapoznaj się z nową sekcją, którą dodałem na Dolny.
Używam małego skryptu perla, który nazywam „nieposortowanym”:
#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
Mam również wersję rozdzielaną przez NULL, o nazwie „unsort0” ... przydatną do użycia z find -print0 i tak dalej.
PS: Zagłosowałem też na „shuf”, nie miałem pojęcia, że jest tam teraz w coreutils… powyższe może być nadal przydatne, jeśli twój system nie ma „shuf”.
<STDIN>
ze <>
w celu dokonania prac rozwiązanie z wejściem z plików też.
Oto pierwsza próba, która jest łatwa dla kodera, ale trudna dla procesora, który przygotowuje losową liczbę do każdej linii, sortuje je, a następnie usuwa losową liczbę z każdej linii. W efekcie linie są sortowane losowo:
cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled
head myfile | awk ...
. Potem zmieniam go na kota; dlatego tam zostało.
-k1 -n
sortować, ponieważ dane wyjściowe awk rand()
są dziesiętne od 0 do 1, a wszystko, co się liczy, to że w jakiś sposób zostanie zmieniony porządek. -k1
może to przyspieszyć, ignorując resztę wiersza, chociaż dane wyjściowe rand () powinny być na tyle unikalne, aby zewrzeć porównanie.
cat filename |
(lub < filename |
) niż pamiętać, jak każdy program pobiera dane wejściowe (lub nie).
oto skrypt 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
wynik
$ 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
z sort
i cut
. Dla nie więcej niż kilku tysięcy linii nie ma to większego znaczenia, ale przy większej liczbie linii ma to znaczenie (próg zależy od zastosowanej awk
implementacji). Nieznaczne uproszczeniem byłoby zastąpienie linii while (1){
i if (e==d) {break}
z while (e<d)
.
Jednowierszowy dla Pythona:
python -c "import random, sys; lines = open(sys.argv[1]).readlines(); random.shuffle(lines); print ''.join(lines)," myFile
A do drukowania tylko jednej losowej linii:
python -c "import random, sys; print random.choice(open(sys.argv[1]).readlines())," myFile
Ale zobacz ten post, aby poznać wady Pythona random.shuffle()
. Nie działa dobrze z wieloma (więcej niż 2080) elementami.
/dev/urandom
robi. Aby wykorzystać ją od Python: random.SystemRandom().shuffle(L)
.
.readLines()
zwraca linie z końcowym znakiem nowej linii.
Prosta funkcja oparta na awk wykona zadanie:
shuffle() {
awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8-
}
stosowanie:
any_command | shuffle
Powinno to działać na prawie każdym systemie UNIX. Testowane na systemach Linux, Solaris i HP-UX.
Aktualizacja:
Zauważ, że wiodące zera ( %06d
) i rand()
mnożenie sprawiają, że działa poprawnie również w systemach, w których sort
nie rozumie liczb. Można go sortować według porządku leksykograficznego (inaczej zwykłe porównywanie ciągów).
"$@"
, będzie również działać z plikami jako danymi wejściowymi. Nie ma powodu do mnożenia rand()
, ponieważ sort -n
jest w stanie sortować ułamki dziesiętne. Jest to jednak dobry pomysł do sterowania awk
formatem wyjściowym jest, bo z domyślnego formatu, %.6g
, rand()
wyjściem Czy liczba okazjonalne w wykładniczej notacji. Podczas gdy przetasowanie do 1 miliona linii jest wystarczające w praktyce, łatwo jest obsłużyć więcej linii bez płacenia znacznej kary za wydajność; np %.17f
.
sort
powinien być w stanie obsługiwać ułamki dziesiętne (nawet z tysiącami separatorów, jak właśnie zauważyłem).
Ruby FTW:
ls | ruby -e 'puts STDIN.readlines.shuffle'
puts ARGF.readlines.shuffle
, możesz sprawić, by działał zarówno z argumentami wejścia standardowego, jak i nazwą pliku.
ruby -e 'puts $<.sort_by{rand}'
- ARGF jest już wyliczalny, więc możemy przetasować linie, sortując je według losowych wartości.
Jedna linijka dla Pythona oparta na odpowiedzi Scai , ale a) przyjmuje standardowe wejście , b) sprawia, że wynik jest powtarzalny z ziarnem, c) wybiera tylko 200 wszystkich linii.
$ cat file | python -c "import random, sys;
random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \
> 200lines.txt
Prostym i intuicyjnym sposobem byłoby użycie shuf
.
Przykład:
Załóż words.txt
jako:
the
an
linux
ubuntu
life
good
breeze
Aby przetasować linie, wykonaj:
$ shuf words.txt
co wyrzuca tasowane linie na standardowe wyjście ; Więc musisz go potokować do pliku wyjściowego, takiego jak:
$ shuf words.txt > shuffled_words.txt
Jeden taki losowy przebieg może dać:
breeze
the
linux
an
ubuntu
good
life
To jest skrypt Pythona, który zapisałem jako rand.py w moim katalogu domowym:
#!/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()
W systemie Mac OSX sort -R
i shuf
nie są one dostępne, więc możesz użyć aliasu w pliku bash_profile jako:
alias shuf='python rand.py'
Jeśli tak jak ja, przyszedłeś tu poszukać alternatywy shuf
dla MacOS, a następnie użyjrandomize-lines
.
Zainstaluj randomize-lines
pakiet (homebrew), który ma rl
polecenie o podobnej funkcjonalności 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
zapewnia shuf
plik binarny jako gshuf
.
Jeśli masz zainstalowaną Scalę, oto linijka do przetasowania danych wejściowych:
ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
Ta funkcja bash ma minimalną zależność (tylko sortowanie i 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
, ale wydajność będzie stanowić problem przy większym nakładzie; użycie jednej $RANDOM
wartości poprawnie tasuje tylko do 32 768 linii wejściowych; chociaż możesz rozszerzyć ten zakres, prawdopodobnie nie warto: na przykład na moim komputerze uruchomienie skryptu na 32768 krótkich wierszach wejściowych zajmuje około 1 sekundy, czyli około 150 razy dłużej niż uruchomienie shuf
i około 10-15 razy tak długo, jak awk
potrzeba własnego rozwiązania PO . Jeśli możesz polegać na sort
byciu obecnym, awk
powinieneś tam również być.
W systemie Windows możesz wypróbować ten plik wsadowy, aby pomóc w tasowaniu danych.txt, użycie kodu wsadowego jest
C:\> type list.txt | shuffle.bat > maclist_temp.txt
Po wydaniu tego polecenia plik maclist_temp.txt będzie zawierał losową listę wierszy.
Mam nadzieję że to pomoże.
Dotychczas nie wspomniano:
unsort
Util. Składnia (nieco zorientowana na listy odtwarzania):
unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic]
[--identity] [--filenames[=profile]] [--separator sep] [--concatenate]
[--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null]
[--linefeed] [file ...]
msort
może tasować po linii, ale zwykle jest to przesada:
seq 10 | msort -jq -b -l -n 1 -c r