How can I revert 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 operating line by line, it would operate block by block. Example:
From
/Section 3/
Rabbits
Dogs
Cats
/Section 2/
Eagles
Mice
/Section 1/
Dogs
Rabbits
Lemmings
To
/Section 1/
Dogs
Rabbits
Lemmings
/Section 2/
Eagles
Mice
/Section 3/
Rabbits
Dogs
Cats
In some files, the beginning of the block is marked by a slash, as in the example above. In others, the blocks are marked only by the existence of one or more blank lines between them.
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 transform the data to a csv
, then sort
the csv
, at last I transform the csv
back to list style.
/Section 1/
Dogs
Rabbits
Lemmings
/Section 2/
Eagles
Mice
/Section 3/
Rabbits
Dogs
Cats
Edit : Sorry, I didn't look at your question very carefully. You can change the sort
command to tac
to reverse the order.
如果有空白行分隔所有块,
awk 'BEGIN{ORS=RS RS;RS=""}{a[NR]=$0}END{for(i=NR;i>0;i--)print a[i]}'
使用csplit
将它们拆分为单独的文件,将生成的文件名放入另一个文件中,然后使用tac
获取要合并的文件名。
What separates blocks in your example? 2 newlines. In Emacs Lisp, if the text is in a string, if you install dash
and s
, you can use one of these 2 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"))
->>
is a dash's threading macro, which pulls s
through successive function invocations. 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, reverses the blocks then saves it back to the same files, you could also install f file manipulation library:
(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 to put the result of the previous computation in argument denoted it
of the next computation. Eg if the result of nreverse
is X
, then the equivalent would be (s-join "\\n\\n" X)
. Finally, suppose you not just want to reverse, but to sort your blocks based on the number after 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 dash
documentation to understand, what -on
, -compose
, -partial
do.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.