유닉스 커맨드 라인이나 쉘 스크립트에서 텍스트 파일의 라인을 섞는 방법은 무엇입니까?


285

텍스트 파일의 줄을 무작위로 섞고 새 파일을 만들고 싶습니다. 파일에는 수천 줄이있을 수 있습니다.

어떻게 함께 그렇게 할 수 cat, awk, cut, 등?



그렇습니다. 원래 질문에도 다른 좋은 대답이 있습니다.
Ruggiero Spearman

그래서 wpa 단어 목록을 만들고 있었습니까? (임의의 추측)
thahgr

답변:


360

사용할 수 있습니다 shuf. 적어도 일부 시스템에서는 (POSIX에없는 것으로 보입니다).

jleedev가 지적했듯이 : sort -R옵션 일 수도 있습니다. 일부 시스템에서는 최소한; 글쎄, 당신은 사진을 얻을. 실제로 섞는 것이 아니라 해시 값에 따라 항목을 정렬 하는 것으로 나타났습니다sort -R .

[편집자 주 : 중복 줄 / 정렬 키가 항상 서로 옆에 있다는 점을 제외하고는 sort -R 거의 섞 습니다 . 다시 말해, 고유 한 입력 라인 / 키만 있으면 진정한 셔플입니다. 출력 순서가 해시 값에 의해 결정되는 것은 사실이지만 , 임의성은 임의 해시 함수 를 선택하여 비롯됩니다 . 설명서를 참조하십시오 .]


31
shuf그리고이 sort -R때문에, 약간 다른 sort -R무작위 순서 요소가있어서 해시 그들 중, sort -R함께있는 동안 반복 요소를 넣어 shuf랜덤 셔플 모든 요소.
SeMeKh

146
OS X 사용자 : brew install coreutils다음 사용 gshuf ...(:
ELLIOTTCABLE

15
sort -Rshuf완전히 다른로 볼 수 있습니다. sort -R결정적입니다. 동일한 입력에서 다른 시간에 두 번 호출하면 동일한 답변을 얻을 수 있습니다. shuf반면에 무작위 출력을 생성하므로 동일한 입력에서 다른 출력을 제공 할 가능성이 높습니다.
EfForEffort

18
맞지 않습니다. "sort -R"은 호출 할 때마다 다른 임의 해시 키를 사용 하므로 매번 다른 출력을 생성합니다.
Mark Pettit

3
무작위성에 대한 참고 사항 : GNU 문서에 따르면, "기본적으로이 명령은 소량의 엔트로피로 초기화 된 내부 의사 난수 생성기를 사용하지만 --random-source = file 옵션과 함께 외부 소스를 사용하도록 지시 할 수 있습니다."
Royce Williams

85

Perl one-liner는 Maxim 솔루션의 간단한 버전입니다.

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

6
OS X에서 셔플하기 위해 별칭을 지정했습니다. 감사합니다!
The Unfun Cat

이것은이 페이지에서 REAL 임의의 행을 반환하는 유일한 스크립트였습니다. 다른 awk 솔루션은 종종 중복 출력을 인쇄했습니다.
Felipe Alvarez

1
당신이 손실됩니다에서 한 줄에 : 그것은 단지 : 다른 라인과 결합되기 때문에하지만 조심해야 해
JavaRunner

@JavaRunner : 후행없이 입력에 대해 이야기하고 있다고 가정합니다 \n. 네, 그 \n존재해야합니다 - 그것은 일반적 이다 - 그렇지 않으면 당신은 당신이 설명하는 것을 얻을 수 있습니다.
mklement0

1
훌륭하게 간결합니다. 나는 대체 제안 <STDIN><>솔루션의 입력으로 작동하므로, 파일 이 너무.
mklement0

60

이 답변은 다음과 같은 방식으로 기존의 많은 훌륭한 답변을 보완합니다.

  • 기존 답변은 유연한 쉘 함수 로 패키지됩니다 .

    • 함수 stdin입력뿐만 아니라 파일 이름 인수도 사용합니다.
    • 이 함수 SIGPIPE141 노이즈를 제거하는 것과는 달리 일반적인 방법 으로 처리하기 위해 추가 단계를 수행합니다 (종료 코드로 조용한 종료 ). 이것은 배관을 할 때와 같이 일찍 닫힌 파이프로 기능 출력을 배관 할 때 중요합니다 head.
  • 성능을 비교 한다.


  • POSIX 호환 기능을 기반으로 awk, sort그리고cut 는에서 적응 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))
' "$@"; }

이 기능 의 Windows 버전은 하단 섹션을 참조하십시오 .

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

성능 비교 :

참고 :이 수치는 OSX 10.10.3을 실행하는 3.2GHz Intel Core i5 및 Fusion Drive가 장착 된 2012 년 말 iMac에서 구했습니다. 타이밍은 사용 된 OS, 기계 사양, awk사용 된 구현 (예 : awkOSX에서 사용되는 BSD 버전이 일반적으로 GNU보다 느리고 awk특히 mawk)에 따라 달라질 수 있지만 , 이는 상대적 성능에 대한 일반적인 의미를 제공해야합니다 .

입력 파일은로 만든 1 백만 줄 파일 입니다 seq -f 'line %.0f' 1000000.
시간은 오름차순으로 나열됩니다 (가장 빠른 것부터).

  • shuf
    • 0.090s
  • 루비 2.0.0
    • 0.289s
  • 5.18.2
    • 0.589s
  • 파이썬
    • 1.342s파이썬 2.7.6; 2.407s파이썬 3.4.2에서 (!)
  • awk+ sort+cut
    • 3.003sBSD와 함께 awk; 2.388sGNU awk(4.1.1) 와 함께 ; 1.811smawk(1.3.4);

추가 비교를 위해 위의 기능으로 패키지화되지 않은 솔루션 :

  • sort -R (중복 입력 라인이있는 경우 진정한 셔플이 아닙니다)
    • 10.661s -더 많은 메모리를 할당해도 차이가없는 것 같습니다
  • 스칼라
    • 24.229s
  • bash 루프 + sort
    • 32.593s

결론 :

  • 사용은 shuf, 당신이 할 수있는 경우 - 그것은 지금까지 가장 빠른입니다.
  • 루비는 물론, 다음 않습니다 .
  • 파이썬 은 루비와 펄보다 눈에 띄게 느리고, 파이썬 버전을 비교하면 2.7.6은 3.4.1보다 약간 빠릅니다.
  • POSIX 호환 awk+ sort+ cut콤보를 최후의 수단으로 사용하십시오 . 어떤 awk구현을 사용 하는지 중요합니다 ( mawkGNU보다 빠르며 awkBSD awk가 가장 느립니다).
  • 멀리 sort -R, bash루프 및 스칼라.

Windows 버전의 Python 솔루션 (Python 코드는 Windows에서 지원되지 않는 신호 관련 명령문의 인용 및 변형을 제외하고는 동일합니다) :

  • PowerShell의 경우 (Windows PowerShell의 $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-Randomcmdlet을 통해 섞을 수 있습니다 (성능에 문제가있을 수 있음). 예 :
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))" %*

SIGPIPE는 Windows에 존재하지 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 : 고맙지 만 from signal import signal, SIGPIPE, SIG_DFL; signal(SIGPIPE, SIG_DFL);원래 솔루션을 생략 하는 것으로 충분하며 파일 이름 인수 를 전달할 수있는 유연성을 유지합니다 -다른 것을 변경할 필요가 없습니다 (인용 제외)-에 추가 한 새로운 섹션을 참조하십시오 바닥.
mklement0

27

나는 "정렬하지 않은"작은 펄 스크립트를 사용한다 :

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

또한 "unsort0"이라는 NULL로 구분 된 버전이 있는데 find -print0 등에 사용하기 편리합니다.

추신 : 'shuf'도 투표했습니다. 요즘 coreutils에 있는지 전혀 몰랐습니다 ... 시스템에 'shuf'가 없으면 위의 내용이 여전히 유용 할 수 있습니다.


좋은 점은 RHEL 5.6에는
shuf

1
잘 했어요; 파일의 입력으로 솔루션을 작동시키기 위해로 교체 <STDIN>하는 것이 좋습니다 . <>
mklement0

20

다음은 코더에서는 쉽지만 각 줄에 난수를 추가하고 정렬 한 다음 각 줄에서 난수를 제거하는 CPU에서는 어려운 첫 번째 시도입니다. 실제로 선은 무작위로 정렬됩니다.

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

8
UUOC. 파일을 awk 자체에 전달하십시오.
ghostdog74

1
맞습니다 head myfile | awk ....로 디버깅합니다 . 그런 다음 그냥 고양이로 바꿉니다. 그것이 그곳에 남아있는 이유입니다.
Ruggiero Spearman

-k1 -nawk의 출력은 rand()0과 1 사이의 십진수 이기 때문에 정렬 할 필요가 없으며 중요한 것은 어떻게 든 순서가 재정렬되기 때문입니다. -k1rand ()의 출력은 비교를 단락시킬만큼 고유해야하지만 나머지 줄을 무시하여 속도를 높이는 데 도움이 될 수 있습니다.
bonsaiviking

@ ghostdog74 : 소위 cat이라는 쓸모없는 사용은 실제로 파이프 명령간에 일관성을 유지하는 데 유용합니다. 각 단일 프로그램이 파일 입력을받는 방법을 기억하는 것보다 cat filename |(또는 < filename |) 를 유지하는 것이 좋습니다 .
ShreevatsaR

2
shuf () {awk 'BEGIN {srand ()} {print rand () "\ t"$ 0}' "$ @"| 정렬 | cut -f2-;}
Meow

16

여기 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

멋지게 수행하지만보다 훨씬 느린 실제로 영업 자신의 대답 , 콤바인 awksortcut. 수천 줄을 넘지 않으면 큰 차이가 없지만 줄 수가 많을수록 중요합니다 (임계 값은 awk사용 된 구현에 달려 있습니다 ). 약간의 단순화는 라인을 교체하는 것 while (1){if (e==d) {break}함께를 while (e<d).
mklement0

11

파이썬을위한 하나의 라이너 :

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 개 이상의) 요소에서는 잘 작동하지 않습니다.


2
"단점"은 파이썬에만 국한되지 않습니다. 유한 PRNG 기간은 PRNG를 시스템과 같은 엔트로피로 다시 시드함으로써 해결할 수 있습니다 /dev/urandom. 파이썬에서 활용하려면 : random.SystemRandom().shuffle(L).
jfs

join ()이 '\ n'에 있어야 할 필요가 없으므로 줄이 각각 인쇄됩니다.
elig

@elig : 아니요 . 후행 줄 바꿈이 .readLines()있는 줄 반환 하기 때문 입니다.
mklement0

9

간단한 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숫자를 이해하지 못하는 시스템에서도 올바르게 작동 합니다. 사전 식 순서 (일명 일반 문자열 비교)를 통해 정렬 할 수 있습니다.


OP 자체의 답변을 함수로 패키징하는 것이 좋습니다. 를 추가하면 파일 로 입력 "$@"작업도 수행됩니다 . 곱셈에 대한 이유가 없습니다 때문에, 소수의 분수를 정렬 할 수 있습니다. 그것은, 그러나, 컨트롤에 대한 좋은 아이디어입니다 때문에 기본 형식으로의 출력 형식 , 출력됩니다에서 가끔 수를 지수 표기법. 실제로는 최대 1 백만 라인을 섞는 것으로 충분하지만 성능 저하없이 많은 라인을 쉽게 지원할 수 있습니다. 예 . rand()sort -nawk%.6grand()%.17f
mklement0

1
@ mklement0 글을 쓰는 동안 OPs 답변을 보지 못했습니다. rand ()에 10e6을 곱하면 내가 기억하는 한 solaris 또는 hpux 정렬과 작동합니다. "$ @"를 사용한 좋은 아이디어
Michał Šrajer

1
감사합니다. 아마도 당신은 답 자체에 곱셈에 대한이 이론적 근거를 추가 할 수 있습니다. POSIX에 따르면 일반적으로 sort소수 분수를 처리 할 수 ​​있습니다 (방금 알았 듯이 수천 개의 구분 기호를 사용하더라도).
mklement0

7

루비 FTW :

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

1
좋은 물건; 을 사용 puts ARGF.readlines.shuffle하면 stdin 입력 및 파일 이름 인수 모두에서 작동하도록 할 수 있습니다.
mklement0

더 짧아 ruby -e 'puts $<.sort_by{rand}'-ARGF는 이미 열거 가능하므로 임의의 값으로 정렬하여 라인을 섞을 수 있습니다.
akuhn

6

scai의 답변을 기반으로 한 Python 용 라이너 하나 이지만 a) stdin을 사용하고 b) seed로 결과를 반복 가능하게 만듭니다 .c) 모든 행 중 200 개만 선택합니다.

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

6

간단하고 직관적 인 방법은 사용하는 것 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

4

우리는 그 일을하기위한 패키지를 가지고 있습니다 :

sudo apt-get install randomize-lines

예:

순서가 지정된 숫자 목록을 만들어 1000.txt에 저장하십시오.

seq 1000 > 1000.txt

셔플하려면 간단히

rl 1000.txt

3

이것은 내 홈 폴더에 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()

맥 OSX에 sort -Rshuf당신이 같은 bash_profile이 별칭을 할 수 있도록 사용할 수 없습니다 :

alias shuf='python rand.py'

3

나처럼 당신이 여기 shufmacOS 에 대한 대안을 찾기 위해 온 다면를 사용하십시오 randomize-lines.

기능이 유사한 명령 randomize-lines이있는 (homebrew) 패키지를 설치하십시오 .rlshuf

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
와 함께 Coreutils를 설치 brew install coreutils하면 shuf바이너리가로 제공 됩니다 gshuf.
shadowtalker

2

스칼라를 설치 한 경우 입력을 섞을 수있는 1 개의 라이너가 있습니다.

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

간단하지만 Java VM을 시작하지 않으면 시작 비용이 상당합니다. 큰 줄 수에서도 잘 수행되지 않습니다.
mklement0

1

이 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
}

OP의 자체 awk지원 솔루션 과 유사한 멋진 bash 솔루션 이지만 더 큰 입력에서는 성능에 문제가 있습니다. 단일 $RANDOM값을 사용 하면 최대 32,768 개의 입력 라인 만 올바르게 섞습니다. 예를 들어 내 컴퓨터에서 32,768 개의 짧은 입력 줄에서 스크립트를 실행하는 데 약 1 초가 shuf걸리며 실행 시간보다 약 150 배 , 약 10-15 배 OP의 자체 awk지원 솔루션이 필요한 한 당신 sort이 존재 하는 것에 의존 할 수 있다면 , awk거기도 있어야합니다.
mklement0

0

Windows 에서이 배치 파일 을 사용하여 data.txt를 섞을 수 있습니다. 배치 코드 사용법은 다음과 같습니다.

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

이 명령을 실행하면 maclist_temp.txt에 무작위 행 목록이 포함됩니다.

도움이 되었기를 바랍니다.


큰 파일에는 작동하지 않습니다. 나는 1 백만 + 라인 파일을 위해 2 시간 후 포기
Stefan Haberl

0

아직 언급되지 않은 내용 :

  1. unsortUTIL. 구문 (일부 재생 목록 중심) :

    unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic]
           [--identity] [--filenames[=profile]] [--separator sep] [--concatenate] 
           [--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null] 
           [--linefeed] [file ...]
  2. msort 한 줄씩 섞을 수 있지만 일반적으로 과잉입니다.

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

0

다른 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];
  }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.