Reverse input order with sed

2019-04-09 10:19发布

I have a file, lets call it 'a.txt' and this file contains the following text line

do to what

I'm wondering what the SED command is to reverse the order of this text to make it look like

what to do

Do I have to do some sort of append? Like append 'do' to 'to' so it would look like

to ++ do (used ++ just to make it clear)

6条回答
叼着烟拽天下
2楼-- · 2019-04-09 10:21

This might work for you (GNU sed):

sed -r 'G;:a;s/^\n//;t;s/^(\S+|\s+)(.*)\n/\2\n\1/;ta' file

Explanation:

  • G add a newline to the end of the pattern space (PS)
  • :a loop name space
  • s/^\n//;t when the newline is at the front of the PS, remove it and print line
  • s/^(\S+|\s+)(.*)\n/\2\n\1/;ta insert either a non-space or a space string directly after the newline and loop to :a

The -r switch makes the regexp easier-on-the-eye (grouping (...), alternation ...|... and the metacharacter for one-or-more + are relieved of the need of a backslash prefix).

查看更多
放我归山
3楼-- · 2019-04-09 10:26

answer

As this question was tagged , my 1st answer was:

First (using arbitraty _ to mark viewed spaces, when a.txt contain do to what:

sed -e '
    :a;
    s/\([^_]*\) \([^ ]*\)/\2_\1/;
    ta;
    y/_/ /;
   ' a.txt
what to do

than, when a.txt contain do to to what:

sed -e '
    :a;
    s/^\(\|.* \)\([^+ ]\+\) \2\([+]*\)\(\| .*\)$/\1\2\3+\4/g;
    ta;
    :b;
    s/\([^_]*\) \([^ ]*\)/\2_\1/;
    tb;
    y/_/ /;
   ' <<<'do to to to what'
what to++ do

There is one + for each supressed duplicated word:

sed -e ':a;s/^\(\|.* \)\([^+ ]\+\) \2\([+]*\)\(\| .*\)$/\1\2\3+\4/g;ta;
        :b;s/\([^_]*\) \([^ ]*\)/\2_\1/;tb;
        y/_/ /;' <<<'do do to what what what what'
what+++ to do+

answer

But as there is a lot of people searching for simple solutions, there is a simple way:

xargs < <(uniq <(tac <(tr \  \\n <<<'do do to what what what what')))
what to do

this could be written:

tr \  \\n <<<'do do to what what what what' | tac | uniq | xargs 
what to do

or even with some scripting:

revcnt () { 
    local wrd cnt plut out="";
    while read cnt wrd; do
        printf -v plus %$((cnt-1))s;
        out+=$wrd${plus// /+}\ ;
    done < <(uniq -c <(tac <(tr \  \\n )));
    echo $out
}

Will do:

revcnt <<<'do do to what what what what' 
what+++ to do+

Or as pure

revcnt() { 
    local out i;
    for ((i=$#; i>0; i--))
    do
        [[ $out =~ ${!i}[+]*$ ]] && out+=+ || out+=\ ${!i};
    done;
    echo $out
}

where submited string have to be submitted as argument:

revcnt do do to what what what what
what+++ to do+

Or if prossessing standard input (or from file) is required:

revcnt() { 
    local out i arr;
    while read -a arr; do
        out=""
        for ((i=${#arr[@]}; i--; 1))
        do
            [[ $out =~ ${arr[i]}[+]*$ ]] && out+=+ || out+=\ ${arr[i]};
        done;
        echo $out;
    done
}

So you can process multiple lines:

revcnt <<eof
do to what
do to to to what
do do to what what what what
eof
what to do
what to++ do
what+++ to do+
查看更多
叼着烟拽天下
4楼-- · 2019-04-09 10:31

I would use awk to do this:

awk '{ for (i=NF; i>=1; i--) printf (i!=1) ? $i OFS : $i "\n" }' file.txt

Results:

what to do

EDIT:

If you require a one-liner to modify your file "in-place", try:

{ rm file.txt && awk '{ for (i=NF; i>=1; i--) printf (i!=1) ? $i OFS : $i "\n" }' > file.txt; } < file.txt
查看更多
神经病院院长
5楼-- · 2019-04-09 10:34

As Bernhard said, tac can be used here:

#!/usr/bin/env bash
set -eu
echo '1 2 3
2 3 4
3 4 5' | while IFS= read -r; do
    echo -n "$REPLY " | tac -s' '
    echo
done

$ ./1.sh
3 2 1 
4 3 2 
5 4 3 

I believe my example is more helpful.

查看更多
forever°为你锁心
6楼-- · 2019-04-09 10:38

May be you would like perl for this:

perl -F -lane '@rev=reverse(@F);print "@rev"' your_file
查看更多
倾城 Initia
7楼-- · 2019-04-09 10:47

I know tac can do something related

$ cat file
 do to what
$ tac -s' ' file

what to do  $

Where the -s defines the separator, which is by default a newline.

查看更多
登录 后发表回答