Перемешивание многострочных файлов

Перемешивание многострочных файлов

У меня есть текстовый файл с пустыми строками, разделяющими блоки текста. Я хотел бы использовать инструменты командной строки *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

В POSIX вы можете сделать что-то вроде:

<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()для заполнения генератора псевдослучайных чисел используется время эпохи unix, поэтому вы можете получить тот же результат, если выполните его дважды в одну и ту же секунду (историческая ошибка теперь запечатлена в спецификации 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:

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";

Связанный контент