2

I have a file and I want to reverse all word in it.

ex input file:

DCBA. HGFE.GI
MLK,PON.RQ
UTS. ZYXWV. 321

ex output file: (What I Want)

ABCD. EFGH.IG
KLM,NOP.QR
STU. VWXYZ. 123

With this sed script: sed '/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D;s/.//' all the entire line is reversed.

The wrong output produced by the command above:

IG.EFGH .ABCD    
QR.NOP,KLM    
123 .VWXYZ .STU 

How can I get my desired output?

5
  • I don't understand your second sentence, it sounds like you don't want to reverse certain lines, but I can't tell which ones. Could you clarify?
    – Seth
    Aug 22, 2014 at 14:45
  • There should be a . in his second line of input.
    – xyz
    Aug 22, 2014 at 14:57
  • As it stands, this isn't going to work. At the end of line one, you want to over-run into the next line. At the end of line two, you want to assume an implied end of sentence. To have it both ways is beyond text-processing, and requires a tool which understands grammar.
    – Shaun
    Aug 22, 2014 at 15:00
  • You didn't clarify your second sentence, but I think I get it now. Are all the "words" going to have spaces between them?
    – Seth
    Aug 22, 2014 at 15:09
  • @Seth No it can be have no space between them Aug 22, 2014 at 15:18

3 Answers 3

7

Try this one in pure bash

#!/bin/bash
while IFS= read -N1 char; do
    if [[ "$char" =~ [[:alnum:]] ]]; then
        word="$char$word"
    else
        echo -n "$word$char"
        word=""
    fi
done <input.file >output.file

Output :

ABCD. EFGH.IG
KLM,NOP.QR
STU. VWXYZ. 123

This code logic is quite simple. We read the file character by character (thanks to the -N1 flag. The IFS= is needed to be able to read newlines as well), then we add every valid character we encounter at the begining of the $word variable (thus reversing the word). When we encounter a non-alphanumeric character, we echo the value of $word and reset the value to empty. We repeat those steps until the end of the file

2
  • 1
    Glad it helped ;) I removed it when I updated my answer with [[:alnum:]], as this is a special class that include also non-english characters
    – Aserre
    Aug 22, 2014 at 16:29
  • I'm surprised to read that the perl solution didn't work for non-English words. Under the hood, I would expect it and the bash solution to use almost identical sets of characters (perl's \w versus [:alnum:] should differ only by underscore). What is the output of env | grep '^L\(C\|ANG\)'? Aug 22, 2014 at 18:51
4

So, you really want to reverse each word, not each sentence, right? Or is your comma supposed to be a period?

perl -pe 's/(\w+)/ reverse $1 /eg' << END
DCBA. HGFE.GI
MLK,PON.RQ
UTS. ZYXWV. 321
END
ABCD. EFGH.IG
KLM,NOP.QR
STU. VWXYZ. 123

If you want to redirect, do this:

perl -pe 's/(\w+)($|\W+)/ reverse($1) . $2 /eg' < file_in > file_out

With my example, do

perl -pe 's/(\w+)($|\W+)/ reverse($1) . $2 /eg' << END > file_out

DO NOT put the redirection on the same line as the terminating word of the heredoc. If you do, the shell will not be able to find the end of the heredoc.

With redirecting to and from files, never do this: command < file > file with the same file. The redirections happen before the command starts, and > will truncate the file to zero length. When the command starts, the input file has already been destroyed.

2
  • @KasiyA simply use a redirection. On the last line, just write END >output.file (this is valid for other answers as well). Note that glenn's answer uses a heredoc (the <<END ... END syntax). If you have a real file, you should call his command like this : perl -pe '... /eg'<input.file >output.file
    – Aserre
    Aug 22, 2014 at 16:03
  • 2
    Don't put the redirection on the last line, use the first line. Explained in my answer Aug 22, 2014 at 16:37
1

And the verbose (python) option:

#!/usr/bin/env python3

with open("/path/to/sourcefile") as messed_up:
    lines = [list(line.replace("\n", "")) for line in messed_up]

def reverse_line(l):
    i=0; line = ""; sep = (" ", ",",".")
    while i < len(l):
        word = []
        while not l[i] in sep:
            word.append(l[i]); i = i+1
            if i == len(l):
                line = line + ("").join(reversed(word)); break
        else:
            line = line + ("").join(reversed(word)) + l[i]
        i = i+1
    print(line)

for l in lines:
    reverse_line(l)

Outputs:

ABCD. EFGH.IG
KLM,NOP.QR
STU. VWXYZ. 123

Lines are "built up" from left to right; characters in the sep list (spaces, commas, dots or any other defined-) and single other characters are placed directly into the line, a consecutive number of other characters are collected first and reversed before they are added to the line.

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .