обратный порядок абзацев в файле

Попробуйте Дугу Linux. Я обычно смущаюсь рекомендовать это новичкам, но это, конечно, соответствует Вашим требованиям и может быть сделано довольно маленьким только с основными пакетами.

8
03.08.2015, 10:54
6 ответов

Используя 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 один) требует пустой строки в конце входного файла для работы как ожидалось.

6
27.01.2020, 20:09

Это решение использует обоих tac и perl прочитать абзац за один раз. Это не требует чтения целого файла в память.

tac file | perl -00 -lpe '$_ = join "\n", reverse split /\n/'

Инвертируйте все строки файла, затем для каждого обратного абзаца, инвертируйте строки.

6
27.01.2020, 20:09
  • 1
    Это выглядит очень изящным и эффективным. Однако это решение также уплотняет несколько пустых (т.е. разделение) строки в один –  Martin Vegter 16.02.2014, 16:08

Мог бы быть способ сделать это с sed, но я сомневаюсь, что это будет просто. Вот то, как я сделал бы это в Perl:

perl -n00e 'push @paragraphs,$_; END{print for reverse @paragraphs}' your_file

Это работает потому что, определяя входной разделитель записей как нулевой символ (-00) говорит Perl работать в режиме абзаца. Определение Perl paragraph1 соответствует Вашему определению точно.


1Look в соответствии с заголовком Other values for $/

3
27.01.2020, 20:09
  • 1
    это работает действительно. Единственная небольшая проблема, она не сохраняет несколько пустых строк, разделяющих абзацы. Вместо этого все абзацы разделяются точно одной пустой строкой. –  Martin Vegter 16.02.2014, 15:41
gem install facets

ruby -r facets/string \
     -e 'puts $stdin.read.strip.shatter(/\n\n+/).reverse.join("")' < file

Это должно сохранить Ваше расстояние между абзацами (будучи более читаемым, чем sed :)), Хотя, опоры к devnull для потрясающего ответа.

0
27.01.2020, 20:09

Если ваши абзацы всегда разделяются одной пустой строкой:

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 ) .

1
27.01.2020, 20:09

Вы МОЖЕТЕ сделать это с помощью одного экземпляра 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 / . Без этого он не был бы буквальным однострочным, поскольку он содержал бы обратную косую черту-новую строку.)

{{ 1}}
1
27.01.2020, 20:09

Теги

Похожие вопросы