Попробуйте Дугу Linux. Я обычно смущаюсь рекомендовать это новичкам, но это, конечно, соответствует Вашим требованиям и может быть сделано довольно маленьким только с основными пакетами.
Используя sed
не вполне так просто как упомянутый Joseph R.. Однако Вы могли сказать:
sed '/./{H;d;};x;s/\n/={NL}=/g' inputfile | \
sed -e 's/^={NL}=//' -e '1!G;h;$!d' | \
sed G | sed 's/={NL}=/\'$'\n/g'
Учитывая демонстрационный вход:
Para 1 line 1
Para 1 line 2
Para 1 line 3
Para 2 line 1
Para 2 line 2
Para 2 line 3
Para 3 line 1
Para 3 line 2
Para 3 line 3
это произвело бы:
Para 3 line 1
Para 3 line 2
Para 3 line 3
Para 2 line 1
Para 2 line 2
Para 2 line 3
Para 1 line 1
Para 1 line 2
Para 1 line 3
Стоит упомянуть, что это решение (а также альтернативный Perl один) требует пустой строки в конце входного файла для работы как ожидалось.
Это решение использует обоих tac
и perl
прочитать абзац за один раз. Это не требует чтения целого файла в память.
tac file | perl -00 -lpe '$_ = join "\n", reverse split /\n/'
Инвертируйте все строки файла, затем для каждого обратного абзаца, инвертируйте строки.
Мог бы быть способ сделать это с sed
, но я сомневаюсь, что это будет просто. Вот то, как я сделал бы это в Perl:
perl -n00e 'push @paragraphs,$_; END{print for reverse @paragraphs}' your_file
Это работает потому что, определяя входной разделитель записей как нулевой символ (-00
) говорит Perl работать в режиме абзаца. Определение Perl paragraph1 соответствует Вашему определению точно.
1Look в соответствии с заголовком Other values for $/
gem install facets
ruby -r facets/string \
-e 'puts $stdin.read.strip.shatter(/\n\n+/).reverse.join("")' < file
Это должно сохранить Ваше расстояние между абзацами (будучи более читаемым, чем sed
:)), Хотя, опоры к devnull для потрясающего ответа.
Если ваши абзацы всегда разделяются одной пустой строкой:
sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed 's/^\x03//;1s/\x03$//;1!G;h;$!d;$a\' | tr $'\003' \\n
Это довольно легко увидеть, как это работает, если разбить его на части и запустить
sed '/ ^ $ / s / ^ / \ x02 /' infile
, затем sed '/ ^ $ / s / ^ / \ x02 / 'infile | tr \\ n $ '\ 002' $ '\ 003' \\ n
и так далее ...
Если ваши абзацы разделены одной или несколькими пустыми строками, например
Para 1 line 1
Para 1 line 2
Para 2 line 1
Para 3 line 1
Para 3 line 2
Para 4 line 1
Para 4 line 2
Para 5 line 1
и вы хотите изменить порядок абзацев, но сохранить порядок «пустых блоков», вы можете прочитать файл дважды:
1-й: превратить абзацы в отдельные строки (удалив пустые блоки между ) и поменяйте местами и
2-й: превратите пустые блоки в отдельные строки, "индексируя" количество пустых строк в каждом блоке (и удаляя непустые строки)
затем вставьте
результаты и обработайте вывод, чтобы восстановить новые строки:
paste -d $'\004' <(sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed -e '/^\x03$/d;s/^\x03//;s/\x03$//;1!G;h;$!d;$a\') \
<(sed -E '/^$/!d;//{:a;N;/^(\n){1,}$/ba;s/\n/\x02/g;s/(.*)\x02.*/\1/}' infile) \
| sed '$!s/\x04/\n/;$s/\x04$//' | tr $'\003\002' \\n\\n
который выводит:
Para 5 line 1
Para 4 line 1
Para 4 line 2
Para 3 line 1
Para 3 line 2
Para 2 line 1
Para 1 line 1
Para 1 line 2
Если вы не возражаете против лишней завершающей строки в выводе, вы можете удалить последний sed
:
paste -d $'\n' <(sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed -e '/^\x03$/d;s/^\x03//;s/\x03$//;1!G;h;$!d;$a\') \
<(sed -E '/^$/!d;//{:a;N;/^(\n){1,}$/ba;s/\n/\x02/g;s/(.*)\x02.*/\1/}' infile) | \
tr $'\003\002' \\n\\n
Предполагается, что первая и последняя строки не пусты (и во входных данных нет \ x02
, \ x03
или \ x04
) .
Вы МОЖЕТЕ сделать это с помощью одного экземпляра sed
; нет необходимости в трубах. Поскольку sed
выполняет только один проход через документ, и поскольку часть файла, необходимая в качестве начала вывода, находится в конце файла, он потребует хранить весь файл в памяти внутри sed
(в области удержания) - поэтому он может плохо масштабироваться. Но он дает точный ответ на вопрос:
:getpara
${
s/$/\
/
G
s/\n\n$//
q
}
N
/\n$/!bgetpara
G
h
$!d
s/\n\n$//
q
Если нет завершающей новой строки, это все равно работает нормально. Если есть одна завершающая новая строка, она подавляется в выводе (то есть в выводе не будет ведущей новой строки). Если есть (например) 5 завершающих символов новой строки на входе, на выходе будет 4 ведущих символа новой строки.
Пробелы между абзацами сохранены.
Пробелы в пустой строке НЕ обрабатываются как разрыв абзаца, но это особенность, а не ошибка. :)
Вы также можете сделать это как гораздо менее читаемый однострочный:
sed ':k;${;s/\(\(\n\).*\)$/\1\2/;G;s/\n\n$//;q;};N;/\n$/!bk;G;h;$!d;s/\n\n$//;q' inputfile
Хотя это работает только с GNU sed
. (Обратите внимание на сложное использование обратных ссылок для выполнения s / $ / \ n /
. Без этого он не был бы буквальным однострочным, поскольку он содержал бы обратную косую черту-новую строку.)