6

Using the "*" symbol, (doesn't have to be that one, any special character will do in order to indicate), how can I edit the text from this:

*berry
straw
rasp
blue
boysen
*
blahblah
blahblah
blahblah
*berry
straw
blue
*
blah
*table
vege
pingpong
*

To this:

strawberry
raspberry
blueberry
boysenberry
blahblah
blahblah
blahblah
strawberry
blueberry
blah
vegetable
pingpongtable

Every character after the first matching asterisk will be placed on every line until the 2nd asterisk match is found.

Any leads on how I can go about this? (sed or awk would be preferred, but if you can think of another way, please shoot me your code!)

I know how to remove all lines containing an asterisk, it's just the character placement part I can't think of

12

This awk code could be enough:

awk -F'*' 'NF == 2 {label = $2; next} {$0 = $0 label} 1'

To break it down:

  • Use * as the field separator. This way, we can simply examine the number of fields (NF) to determine if the beginning or end of a block is reached.
  • When there are two fields, we save the second field in label and continue to the next line.
  • From then, we append that label to the current line, and then print. If the label is empty, we are outside a block and there's no effect. If not, we get the required output.
  • Absolutely amazing, just what I was looking for. I didn't even need the remove files, since the output is just what I wanted. Thank you for the great explanation too. Cheers, have a good one! – TuxForLife Apr 25 '15 at 2:12
  • 4
    I love awk more and more :3 – Sergiy Kolodyazhnyy Apr 25 '15 at 13:25
8

In sed, you could copy the "special" line into hold space before deleting it

sed -e '/^\*/{h;d;}'

and then append the hold space to each succeeding pattern space, replacing the resulting newline and marker character

    -e '{G;s/\n\*//;}'

Testing it with your data,

$ sed -e '/^\*/{h;d;}' -e '{G;s/\n\*//;}' file
strawberry
raspberry
blueberry
boysenberry
blahblah
blahblah
blahblah
strawberry
blueberry
blah
vegetable
pingpongtable

Note: this doesn't stop when it encounters the second asterisk; it does exactly the same, but it's appending * followed by nothing - until it matches the next *sometext.

  • Interesting, I have never seen the "hold space" tactic, thank you for the new lesson in sed. Your code also worked with my file perfectly, thank you, have a great day! – TuxForLife Apr 25 '15 at 2:16
7

Here's a Perl way:

$ perl -lne '/^\*(.*)/ || print "$_$1"' file
strawberry
raspberry
blueberry
boysenberry
blahblah
blahblah
blahblah
strawberry
blueberry
blah
vegetable
pingpongtable

Explanation

The -n will cause Perl to read each line of the input file, saving it in the special variable $_, the -l will cause it to i) strip trailing newlines (\n) from each line and ii) add a newline to each call of print. The -e is the script that is applied to each line.

  • /^\*(.*)/ : match lines that start with an asterisk and save everything after the asterisk as $1 (that's what the parentheses do).

  • || print "$_$1"' : the || is a logical OR. Therefore, the print will only be executed if the current line did not start with an asterisk. If so, we print the current line ($_) along with whatever is currently saved as $1 (the pattern following the asterisk).


As usual, there are many ways of doing this. A silly and inefficient one, but one which highlights the string manipulation capabilities of the shell, is:

$ while read line; do 
    [[ $line =~ ^\* ]] && pat="${line#\*}" || printf "%s%s\n" "$line" "$pat"; 
  done < file
strawberry
raspberry
blueberry
boysenberry
blahblah
blahblah
blahblah
strawberry
blueberry
blah
vegetable
pingpongtable

Explanation

  • while read line; do ... ; done < file : this is a classic while loop which will read each line of the input file file and save it as $line.
  • [[ $line =~ ^\* ]] && pat="${line#\*}" : if the line starts with an *, remove everything after that (that's what the ${line#\*} does, for more details, see here) and save it as $pat. *|| printf "%s%s\n" "$line" "$pat"; : if the previous command failed (so, the line does not start with an asterisk), print the line and the current value of $pat.

3

Through my favorite Python...

with open('/path/to/the/file') as f:
    counter = False
    for line in f:
        if line.startswith('*') and not counter:
            m = line.strip().lstrip('*')
            counter = True
        elif line.startswith('*') and counter:
            counter = False    
        elif counter:
            if not line.startswith('*'):
                print(line.strip() + m)
        else:
            print(line.strip())  
3

Came here late. Here is another python approach:

#!/usr/bin/env python2
with open('/path/to/file.txt') as f:
    for lines in f.read().split('*'):
        entries = lines.rstrip().split('\n')
        for i in range(1, len(entries)):
            print entries[i] + entries[0]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.