여러 줄 파일 셔플

여러 줄 파일 셔플

텍스트 블록을 구분하는 빈 줄이 있는 텍스트 파일이 있습니다. *NIX 명령줄 도구를 사용하여 블록 구조를 존중하면서 이 파일을 섞고 싶습니다. 즉, 출력에서 ​​블록의 변경된 순서를 보고 싶습니다. 블록 내부의 선과 순서는 동일하게 유지됩니다.

입력 파일 예:

line 1
line 2

line 10
line 20
line 30

line 100
line 200

출력 파일(셔플 후):

line 10
line 20
line 30

line 1
line 2

line 100
line 200

물론, 반복적으로 실행하면 블록의 순서가 달라져야 합니다.

파일의 첫 번째 줄은 항상 비어 있지 않습니다. 이중 빈 줄이 없습니다. 파일의 마지막 줄은 항상 비어 있습니다.

나는 목록 목록의 모든 줄을 읽고 섞은 후 출력하는 매우 간단한 Python 스크립트를 작성했습니다. 표준 *NIX 도구를 사용하여 이를 수행할 수 있는지 궁금합니다.

답변1

POSIXly 다음과 같은 작업을 수행할 수 있습니다.

<file awk '
  BEGIN{srand(); n=rand()}
  {print n, NR, $0}
  !NF {n=rand()}
  END {if (NF) print n, NR+1, ""}' |
  sort -nk1 -k2 |
  cut -d' ' -f3-

즉, 각 줄 앞에 <a-random-number-that-changes-with-each-paragraph>줄 번호를 붙인 다음 첫 번째 번호, 두 번째 번호로 정렬하여 단락의 줄 순서를 유지하고 추가 번호를 제거합니다.

sed '$d'후행 공백 줄을 제거하기 위해 파이프를 사용할 수도 있습니다 .

대부분의 awk구현 에서는 srand()유닉스 에포크 시간을 사용하여 의사 난수 생성기를 시드하므로 동일한 초에 두 번 실행하면 동일한 결과를 얻을 수 있습니다.불행히도 내 노력에도 불구하고 이제 역사적 버그가 POSIX 사양에 새겨졌습니다.).

답변2

GNU 도구를 사용하면 단락을 NUL로 구분된 그룹으로 나누고 섞은 다음 NUL을 제거합니다.

$ sed '1s/^/\n/; s/^$/\x00/' input | shuf -z | sed '1d; s/\x00//'
line 100
line 200

line 10
line 20
line 30

line 1
line 2

NUL을 사용하지 않는 대체 접근 방식

모든 도구가 NUL 문자를 지원하는 것은 아니므로 다음은 대안입니다. 이는 단락을 읽고 ~개행 문자를 대체한 다음 섞은 다음 ~결과를 표시하기 전에 다시 개행 문자로 변환합니다.

$ awk '{gsub(/\n/, "~")} 1' RS= input | shuf | awk '{gsub(/~/, "\n")} 1' ORS="\n\n"
line 10
line 20
line 30

line 100
line 200

line 1
line 2

텍스트에 가 포함될 수 있는 경우 ~텍스트에 포함되지 않는 다른 문자를 임시 줄 구분 기호로 사용하세요.

답변3

펄 사용:

perl -MList::Util -00 -e 'chomp(my @a=<>); print join("\n\n", List::Util::shuffle @a) . "\n";' < input

또는 스크립트 파일로 퍼뜨립니다.

#!/usr/bin/perl
use List::Util 'shuffle';
local $/ = "";  ## paragraph mode
chomp(my @a = <>);
print join("\n\n", shuffle @a) . "\n";

관련 정보