Reverse input order with sed

2019-04-09 10:28发布

问题:

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)

回答1:

sed answer

As this question was tagged sed, 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+

bash answer

But as there is a lot of people searching for simple bash 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 bash 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 bash

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+


回答2:

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.



回答3:

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


回答4:

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



回答5:

May be you would like perl for this:

perl -F -lane '@rev=reverse(@F);print "@rev"' your_file


回答6:

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.