Unix komut satırında veya kabuk komut dosyasında bir metin dosyasının satırlarını nasıl karıştırabilirim?


285

Bir metin dosyasının satırlarını rastgele karıştırmak ve yeni bir dosya oluşturmak istiyorum. Dosyada binlerce satır olabilir.

Nasıl ile yapabilirsiniz cat, awk, cut, vs?



Evet, bu orijinal soruda başka hoş cevaplar da var.
Ruggiero Spearman

yani, bir wpa kelime listesi mi yapıyordunuz? (sadece rastgele bir tahmin)
thahgr

Yanıtlar:


360

Kullanabilirsiniz shuf. Bazı sistemlerde en azından (POSIX'te görünmüyor).

Jleedev'in işaret ettiği gibi: sort -Rbir seçenek de olabilir. Bazı sistemlerde en azından; resim elde edersiniz. Bu sivri dışarı olmuştursort -R gerçekten karıştırmak yerine sıralama öğeler onların karma değerine göre değil.

[Editörün notu: yinelenen satırlar / sıralama anahtarlarının her zaman yan yana olması dışında sort -R neredeyse karışır . Başka bir deyişle: sadece benzersiz giriş satırları / tuşları ile gerçek bir karışıklıktır. Çıktı sırasının hash değerleri tarafından belirlendiği doğru olsa da , rastgelelik rastgele bir hash fonksiyonu seçmekten gelir - kılavuza bakınız .]


31
shufve sort -Rnedeni, biraz farklıdır sort -Rrastgele emir elemanları uygun karma bir tanesi, sort -Rbirbirine tekrarlandı elemanları koymak shufrastgele karıştırır tüm elemanları.
SeMeKh

146
OS X kullanıcıları için:, brew install coreutilssonra şunu kullanın gshuf ...: (:
ELLIOTTCABLE

15
sort -Rve shuftamamen farklı görülmelidir. sort -Rbelirleyicidir. Aynı girişte farklı zamanlarda iki kez çağırırsanız aynı cevabı alırsınız. shufÖte yandan, rastgele çıktı üretir, bu nedenle büyük olasılıkla aynı girdi üzerinde farklı çıktılar verir.
EfForEffort

18
Bu doğru değil. "sort -R" her başlattığınızda farklı bir rasgele karma anahtarı kullanır , böylece her seferinde farklı çıktılar üretir.
Mark Pettit

3
Rasgelelik hakkında not: GNU dokümanlarına göre, "Varsayılan olarak bu komutlar az miktarda entropi ile başlatılan dahili bir sahte rasgele jeneratör kullanır, ancak --random-source = file seçeneğiyle harici bir kaynak kullanmaya yönlendirilebilir."
Royce Williams

86

Perl tek astar, Maxim'in çözümünün basit bir versiyonu olacaktır

perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile

6
OS X'te karıştırmak için bunu taklit ettim. Teşekkürler!
The Unfun Cat

Bu sayfada GERÇEK rastgele satırlar döndüren tek komut dosyasıydı. Diğer awk çözümleri genellikle çift çıktı yazdırılır.
Felipe Alvarez

1
Ancak dikkatli olun çünkü dışarıda bir satır
kaybedeceksiniz

@JavaRunner: Bence iz bırakmadan girdi hakkında konuşuyorsunuz \n; evet, bu \nmevcut olmalı - ve tipik olarak - aksi takdirde tarif ettiğiniz şeyi alırsınız.
mklement0

1
Harika özlü. Ben değiştirmenizi öneririz <STDIN>ile <>çözüm girişi ile çalışır, böylece dosyaları da.
mklement0

60

Bu cevap mevcut birçok harika yanıtı aşağıdaki şekillerde tamamlar:

  • Mevcut cevaplar esnek kabuk fonksiyonlarında paketlenmiştir :

    • İşlevler yalnızca stdingirdi değil , aynı zamanda dosya adı argümanlarını da alır
    • Fonksiyonlar , gürültüyle kırılmanın aksine , normal şekilde (çıkış koduyla sessiz sonlandırma ) işlemek için ekstra adımlar atarSIGPIPE141 . Bu, fonksiyon çıkışını, boruları döşerken olduğu gibi erken kapalı bir boruya bağlarken önemlidir head.
  • Bir performans karşılaştırması yapılır.


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))
' "$@"; }

Bu işlevin Windows sürümü için alt bölüme bakın .

shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
                     puts ARGF.readlines.shuffle' "$@"; }

Performans karşılaştırması:

Not: Bu sayılar, 3.2 GHz Intel Core i5 ve OSX 10.10.3 çalıştıran bir Fusion Drive'a sahip 2012 sonlarında iMac'te elde edildi. Zamanlamalar kullanılan işletim sistemine, makine özelliklerine, awkkullanılan uygulamaya (ör awk. OSX'te kullanılan BSD sürümü genellikle GNU'dan daha yavaştır awkve özellikle mawk) göre değişmekle birlikte , bu genel bir göreceli performans duygusu sağlamalıdır .

Girdi dosyası ile üretilen 1 milyon satırlık bir dosyadırseq -f 'line %.0f' 1000000 .
Zamanlar artan sırada listelenir (en hızlısı önce):

  • shuf
    • 0.090s
  • Yakut 2.0.0
    • 0.289s
  • Perl 5.18.2
    • 0.589s
  • piton
    • 1.342sPython 2.7.6 ile; 2.407s(!) Python 3.4.2 ile
  • awk+ sort+cut
    • 3.003sBSD ile awk; 2.388sGNU awk(4.1.1) ile; 1.811sile mawk(1.3.4);

Daha fazla karşılaştırma için, yukarıdaki fonksiyonlar olarak paketlenmemiş çözümler:

  • sort -R (yinelenen giriş satırları varsa gerçek bir karıştırma değil)
    • 10.661s - daha fazla bellek ayırmanın bir fark yarattığı görülmüyor
  • Scala
    • 24.229s
  • bash döngüler + sort
    • 32.593s

Sonuç :

  • Kullan shuf, eğer yapabilirsen - açık arayla en hızlı.
  • Ruby başarılı olur, ardından Perl gelir .
  • Python , Ruby ve Perl'den belirgin şekilde daha yavaştır ve Python sürümlerini karşılaştırırken 2.7.6, 3.4.1'den biraz daha hızlıdır
  • POSIX uyumlu awk+ sort+ cutcombo'yu son çare olarak kullanın ; hangi awkuygulamayı kullandığınız önemlidir ( GNU'dan mawkdaha hızlı awk, BSD awken yavaştır).
  • Uzak durun sort -R, bashdöngüler ve Scala.

Python çözümünün Windows sürümleri (Python kodu, Windows'ta desteklenmeyen tırnak işaretleri ve sinyalle ilgili ifadelerin kaldırılması hariç) aynıdır:

  • PowerShell için (Windows PowerShell'de, $OutputEncodingASCII olmayan karakterleri ardışık düzen yoluyla göndermek isteyip istemediğinizi ayarlamanız gerekir ):
# 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'in Get-Randomcmdlet'i aracılığıyla yerel olarak karışabileceğini unutmayın (performans bir sorun olabilir); Örneğin:
Get-Content someFile.txt | Get-Random -Count ([int]::MaxValue)

  • İçin cmd.exe(toplu iş dosyası):

Dosyaya kaydet shuf.cmd, örneğin:

@echo off
python -c "import sys, random, fileinput; lines=[line for line in fileinput.input()]; random.shuffle(lines); sys.stdout.write(''.join(lines))" %*

SIGPIPE Windows'ta mevcut değil, bunun yerine bu basit tek astarı kullandım: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]));"
elig

@elig: Teşekkürler, ancak from signal import signal, SIGPIPE, SIG_DFL; signal(SIGPIPE, SIG_DFL);orijinal çözümden atlamak yeterlidir ve dosya adı argümanlarını da geçirme esnekliğini korur - başka bir şeyi değiştirmeye gerek yoktur (alıntı dışında) - lütfen eklediğim yeni bölüme bakın alt.
mklement0

27

Ben "unsort" dediğim küçük bir perl betiği kullanın:

#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);

Ayrıca find -print0 ve benzeri ile kullanmak için kullanışlı "unsort0" adlı bir NULL sınırlandırılmış sürümü var.

Not: Ben de 'shuf' seçtim, bugünlerde coreutils orada olduğunu bilmiyordum ... sistemleriniz 'shuf' yoksa yukarıdakiler yine de yararlı olabilir.


iyi olanı, RHEL 5.6'nın shuf'u yok (
Maxim Egorushkin

1
Güzel yapılmış; Çözümün dosyalardan gelen girdilerle de çalışmasını sağlamak için <STDIN>ile değiştirmenizi öneririm . <>
mklement0

20

Burada, kodlayıcıda kolay olan ancak her satıra rasgele bir sayı ekleyen, sıralayan ve daha sonra rasgele sayıyı her satırdan ayıran CPU üzerinde zor olan bir ilk deneme var. Aslında, çizgiler rastgele sıralanır:

cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled

8
UUOC. kendisini awk için iletin.
ghostdog74

1
Doğru, hata ayıkladım head myfile | awk .... Sonra onu kedi olarak değiştiriyorum; bu yüzden orada kaldı.
Ruggiero Spearman

-k1 -nSıralamaya gerek yok , çünkü awk'ın çıktısı rand()0 ile 1 arasında bir ondalık sayıdır ve tek önemli olan şey bir şekilde yeniden sıralanmasıdır. -k1rand () çıktısının karşılaştırmayı kısa devre yapacak kadar benzersiz olması gerekirken, hattın geri kalanını yoksayarak hızlanmasına yardımcı olabilir.
bonsaiviking

@ ghostdog74: Kedinin yararsız olarak adlandırılan çoğu kullanımı aslında borulu komutlar arasında tutarlı olmak için yararlıdır. Her bir programın dosya girdisini nasıl alacağını (veya almayacağını) hatırlamaktan cat filename |(veya < filename |) saklamak daha iyidir.
ShreevatsaR

2
shuf () {awk 'BEGIN {srand ()} {print rand () "\ t" $ 0}' "$ @" | sırala | cut -f2-;}
Miyav

16

işte garip bir senaryo

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

çıktı

$ cat file
1
2
3
4
5
6
7
8
9
10

$ ./shell.sh
7
5
10
9
6
8
2
1
3
4

Güzel yapılır, ancak pratikte OP'nin ve awkile birleşen kendi cevabından çok daha yavaştır . Birkaç binden fazla satır için çok fazla bir fark yaratmaz, ancak daha yüksek satır sayıları ile önemlidir (eşik kullanılan uygulamaya bağlıdır ). Çizgileri değiştirmek ve ile hafif bir sadeleştirme olacaktır . sortcutawkwhile (1){if (e==d) {break}while (e<d)
mklement0

11

Python için bir astar:

python -c "import random, sys; lines = open(sys.argv[1]).readlines(); random.shuffle(lines); print ''.join(lines)," myFile

Ve sadece tek bir rastgele çizgi yazdırmak için:

python -c "import random, sys; print random.choice(open(sys.argv[1]).readlines())," myFile

Ama bakın bu yazı piton en dezavantajlarından için random.shuffle(). Birçok (2080'den fazla) elemanla iyi çalışmaz.


2
"dezavantaj" Python'a özgü değildir. PRNG'nin sistemdeki gibi entropi ile yeniden beslenmesi ile sonlu PRNG periyotları çözülebilir /dev/urandom. Python onu kullanmak için: random.SystemRandom().shuffle(L).
jfs

join () öğesinin '\ n' üzerinde olması gerekmez, böylece satırların her biri kendi başına yazdırılır mı?
elig

@elig: Hayır, çünkü .readLines()geri dönüş hatları ile bir arka yeni satır.
mklement0

9

Basit awk tabanlı işlev işi yapacak:

shuffle() { 
    awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8-
}

kullanımı:

any_command | shuffle

Bu hemen hemen her UNIX üzerinde çalışmalıdır. Linux, Solaris ve HP-UX üzerinde test edilmiştir.

Güncelleme:

Baştaki sıfırların ( %06d) ve rand()çarpma işleminin sortsayıları anlamayan sistemlerde de düzgün çalışmasını sağladığını unutmayın. Sözcük bilgisi sırasına göre sıralanabilir (normal dize karşılaştırması olarak da bilinir).


OP'nin kendi cevabını bir fonksiyon olarak paketlemesi iyi bir fikir; Eğer eklerseniz "$@", girdi olarak dosyalarla da çalışır . Çoğalmak için bir neden yoktur rand(), çünkü sort -nondalık kesirleri sıralayabilir. Bununla beraber, kontrol etmek için iyi bir fikirdir awk, çünkü varsayılan biçim, ile, 'ın çıkış biçimi %.6g, rand()irade çıkışı ara sıra numarası üstel gösterimde. Uygulamada 1 milyon satıra kadar karıştırmak muhtemelen yeterli olsa da, bir performans cezası ödemeden daha fazla satırı desteklemek kolaydır; örn %.17f.
mklement0

1
@ mklement0 Benimki yazarken OP'nin cevabını fark etmedim. rand (), hatırladığım kadarıyla solaris veya hpux sıralama ile çalışması için 10e6 ile çarpılır. "$ @"
İle

1
Anladım, teşekkürler; belki de cevabın kendisine çarpma için bu mantığı ekleyebilirsiniz; Genel olarak, POSIX'e göre, sortondalık kesirleri işleyebilmelidir (az önce fark ettiğim gibi binlerce ayırıcıyla bile).
mklement0

7

Yakut FTW:

ls | ruby -e 'puts STDIN.readlines.shuffle'

1
Harika şeyler; Eğer kullanırsanız puts ARGF.readlines.shuffle, bunun hem standart girdinin girdi ve dosya adı argümanları ile çalışmak yapabilirsiniz.
mklement0

Daha da kısa ruby -e 'puts $<.sort_by{rand}'- ARGF zaten numaralandırılabilir, bu nedenle çizgileri rastgele değerlere göre sıralayarak karıştırabiliriz.
akuhn

6

Python için scai'nin cevabına dayanan bir astar , ancak a) stdin alır, b) sonucu tohumla tekrarlanabilir hale getirir, c) tüm hatların sadece 200'ünü alır.

$ cat file | python -c "import random, sys; 
  random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \
  > 200lines.txt

6

Kullanmanın basit ve sezgisel bir yolu olurdu shuf.

Misal:

Varsayın words.txt:

the
an
linux
ubuntu
life
good
breeze

Çizgileri karıştırmak için şunları yapın:

$ shuf words.txt

karıştırılan çizgileri standart çıktıya atar ; Yani, hiç ettik borusuna bir karşı kendisine çıkış dosyası gibi:

$ shuf words.txt > shuffled_words.txt

Böyle bir karıştırma çalıştırması aşağıdakileri verebilir:

breeze
the
linux
an
ubuntu
good
life

4

İşi yapmak için bir paketimiz var:

sudo apt-get install randomize-lines

Misal:

Sıralı bir numara listesi oluşturun ve 1000.txt'ye kaydedin:

seq 1000 > 1000.txt

karıştırmak için

rl 1000.txt

3

Bu, ana klasörümde rand.py olarak kaydettiğim bir python betiği:

#!/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 üzerinde sort -Rve shufsen gibi bash_profile bu diğer ad böylece kullanılamaz:

alias shuf='python rand.py'

3

Benim gibi buraya shufmacOS için alternatif aramak için geldiysenizrandomize-lines .

Benzer işleve randomize-linessahip bir rlkomutu olan Install (homebrew) paketi 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

1
İle coreutils yükleme brew install coreutilssağlar shufolarak ikili gshuf.
shadowtalker

2

Scala yüklüyse, girişi karıştırmak için bir astar:

ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'

Çekici bir şekilde basittir, ancak Java VM'nin yine de başlatılması gerekmedikçe, bu başlangıç ​​maliyeti dikkate değerdir; büyük satır sayılarıyla da iyi performans göstermez.
mklement0

1

Bu bash işlevi minimum bağımlılığa sahiptir (yalnızca sıralama ve bash):

shuf() {
while read -r x;do
    echo $RANDOM$'\x1f'$x
done | sort |
while IFS=$'\x1f' read -r x y;do
    echo $y
done
}

OP'nin kendi awkdestekli çözümüne paralel olan güzel bash çözümü , ancak performans daha büyük girdilerle ilgili bir sorun olacaktır; tek bir $RANDOMdeğeri kullanmanız yalnızca 32.768 giriş satırına kadar doğru şekilde karıştırır; bu aralığı genişletebilmenize rağmen, muhtemelen buna değmez: örneğin, makinemde, komut dosyanızı 32.768 kısa giriş satırında çalıştırmak yaklaşık 1 saniye sürer, bu da çalışma süresinin yaklaşık 150 katı shufve yaklaşık 10-15 kez OP'nin kendi awkdestekli çözümü aldığı sürece. Güvenebileceğiniz ise sortvarlık mevcut, awkhem de orada olmalı.
mklement0

0

Windows'da data.txt dosyanızı karıştırmanıza yardımcı olması için bu toplu iş dosyasını deneyebilirsiniz . Toplu iş kodunun kullanımı

C:\> type list.txt | shuffle.bat > maclist_temp.txt

Bu komutu verdikten sonra maclist_temp.txt rastgele bir satır listesi içerecektir.

Bu yardımcı olur umarım.


Büyük dosyalar için çalışmaz. 2 saat sonra 1 milyon + satır dosyadan vazgeçtim
Stefan Haberl

0

Henüz belirtilmemiş:

  1. unsortUtil. Sözdizimi (bir şekilde oynatma listesine yönelik):

    unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic]
           [--identity] [--filenames[=profile]] [--separator sep] [--concatenate] 
           [--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null] 
           [--linefeed] [file ...]
  2. msort satır satır karışabilir, ancak genellikle aşırıya kaçar:

    seq 10 | msort -jq -b -l -n 1 -c r

0

Başka bir awkvaryant:

#!/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];
  }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.