How to change the order of blocks of text

How can I reverse the order of some blocks of text using only bash commands like sed and cat? What I want is something like tac, but instead of working sequentially, it will work on a block basis. Example:

FROM

/Section 3/ Rabbits Dogs Cats /Section 2/ Eagles Mice /Section 1/ Dogs Rabbits Lemmings 

For

 /Section 1/ Dogs Rabbits Lemmings /Section 2/ Eagles Mice /Section 3/ Rabbits Dogs Cats 

In some files, the beginning of the block is marked with a slash, as in the example above. In other cases, blocks are marked only by the presence of one or more blank lines between them.

+7
source share
4 answers

In emacs you can use the sort-paragraphs command:

Ctrl-x h Meta-x sort-paragraphs Enter


In vim : https://superuser.com/questions/365094/sort-file-per-paragraph-in-vim


Use the basic unix tools:

 awk -F'\n' -vRS='' -vOFS=',' '{$1=$1}1' input.txt | sort | tr ',' '\n' | sed ' s@ ^/@\n/@' 

I use awk to convert data to csv , then sort csv , finally I convert csv style to list.


result:

 /Section 1/ Dogs Rabbits Lemmings /Section 2/ Eagles Mice /Section 3/ Rabbits Dogs Cats 

Edit : Sorry, I did not look at your question very carefully. You can change the sort command to tac to reverse the order.

+4
source

If there are blank lines separating all the blocks,

 awk 'BEGIN{ORS=RS RS;RS=""}{a[NR]=$0}END{for(i=NR;i>0;i--)print a[i]}' 
+2
source

Use csplit to split them into separate files, put the generated file names in another file, then use tac to merge the file names.

+1
source

What separates the blocks in your example? 2 new lines. In Emacs Lisp, if the text is in a string, if you set dash and s , you can use one of these two equivalent expressions:

 (s-join "\n\n" (nreverse (s-split "\n\n" s))) ; where s is your string (->> s (s-split "\n\n") nreverse (s-join "\n\n")) 

->> - This is a macro of a rough thread that pulls s through successive function calls. Think * nix pipes : s | s-split "\n\n" | nreverse | s-join "\n\n" s | s-split "\n\n" | nreverse | s-join "\n\n" s | s-split "\n\n" | nreverse | s-join "\n\n" .

If you want to have an Emacs Lisp function that opens a file, cancels the blocks, and then saves them back to the same files, you can also set the file management library f :

 (defun reverse-blocks (f) "F is a filename." (interactive "fFind file: ") ; letter `f` is filename goes in first arg (let ((s (f-read f))) ; read file into a string (--> s s-chomp ; remove trailing newline (s-split "\n\n" it) nreverse (s-join "\n\n" it) (f-write it 'utf-8 f)))) ; write to the same file 

Here I use another trailing macro --> , which allows you to put the result of the previous calculation in the argument indicated by it next calculation. For example. if the result of nreverse is X , then the equivalent will be (s-join "\n\n" X) . Finally, suppose you not only want to cancel, but also sort your blocks based on the number after the word "Section":

 (--sort (< (string-to-number (cadr (s-match "/.*?\\([0-9]\\)/" it))) (string-to-number (cadr (s-match "/.*?\\([0-9]\\)/" other)))) it) ; put it instead of nreverse 

Which, using dash-functional , is equivalent to:

 (--sort (-on '< (-compose 'string-to-number 'cadr (-partial 's-match "/.*?\\([0-9]+\\)/"))) it) ; put it instead of nreverse 

Read the dash documentation to understand what -on , -compose , -partial do.

0
source

All Articles