5

I have a file like here file.txt

bbb-ccc-cccc#
   aasdf  asdas asdasa fgdg
   asdfa  asfdas  adfaq  asfa
   afdaf  fafa  fafd  afafa
bbb-ccc-cccc#

I want to take the word ending in # and I want to add it to each line as the first word.

sed 's/bbb-ccc-cccc#/^/' < file.txt > newfile.txt

I don't know the word before # sign ahead of time, so my point is find the word ending with # and put it at the beginning of each line. For this file.txt I need like here:

bbb-ccc-cccc#
bbb-ccc-cccc#   aasdf  asdas asdasa fgdg
bbb-ccc-cccc#   asdfa  asfdas  adfaq  asfa
bbb-ccc-cccc#   afdaf  fafa  fafd  afafa
bbb-ccc-cccc#
  • Thanks to all for your kind and helpfull and solutionable answer. – prestruggler Feb 19 '17 at 5:17
10

With perl:

perl -lpe 'if (/\H+#/) {$word = $&} else {$_ = $word . $_}'

That is, if we find a sequence of non-blank characters (\H+) followed by a # on a line, we use that ($& is what is matched by the regexp) as the word to be inserted at the beginning of the following lines.

Same with awk:

awk '
  match($0, /[^[:blank:]]+#/) {
    word = substr($0, RSTART, RLENGTH)
    print
    next
  }
  {print word $0}'

Same with sed (using the hold space to store the word):

sed '
  /[^[:blank:]]\{1,\}#/ {
    h; # save the line in the hold space
    s//\
&\
/; # put newlines on each side of the matched word
    s/.*\n\(.*\)\n/\1/; # remove every thing but the word
    x; # swap hold and pattern space so that now the hold
       # space contains the word. And branch off:
    b
  }
  # for the other lines:
  G; # append the hold space to the pattern space
  s/\(.*\)\n\(.*\)/\2\1/; # move the word to the beginning'

If you want to only match on word#s that are at the end of a line, replace # with #$ in all 3 commands above.

  • I wonder if the perl (and awk) regex should be anchored to $ to match end-of-line? – Jeff Schaller Feb 16 '17 at 12:15
  • @Jeff, added a note to that effect. – Stéphane Chazelas Feb 16 '17 at 12:17
  • I'll never not be amazed at how small perl solutions tend to be. – apricot boy Feb 16 '17 at 22:28
7

Using awk:

$ awk '/^[^ ]*#$/ { prefix = $0; print; next } { print prefix, $0 }' file
bbb-ccc-cccc#
bbb-ccc-cccc#    aasdf  asdas asdasa fgdg
bbb-ccc-cccc#    asdfa  asfdas  adfaq  asfa
bbb-ccc-cccc#    afdaf  fafa  fafd  afafa
bbb-ccc-cccc#

This prints each line with a prefix. The prefix is taken from any line that matches the pattern ^[^␣]*#$, i.e. that consist entirely of non-space characters and ends with #. Such lines are printed without an additional prefix and the processing is continuing with the next input line.

3

Here my solution with gnu sed :

sed '/\(.*\)#/{h;:y;n;/\(.*\)#/b;G;s/\(.*\)\n\(.*\)/\2\1/;by}' test.txt

Fast explanation :

  • h copies current buffer to "hold space"
  • G appends "hold space" to current buffer (adding a carryreturn which need to be removed)
  • :xxx are labels
  • bxxx are gotos. "b" alone goes to the end of the script
  • n prints current buffer and reads next line

Here my solution with bash :

while IFS='' read -r x;do if [[ "$x" = *# ]] ; then if [ "$p" = "$x" ]; then p=''; else p="$x"; x=''; fi ; fi; printf '%s%s\n' "$p" "$x";done < test.txt
2

With gnu sed:

sed -n -e ' /.*#$/ { p; h; b; };' \
       -e '{ H; x; s/[\n[:blank:]]\+/ /; p; s/\(.*#\).*/\1/; h; }' \
        file.txt

The general idea is to store the ...# line in the hold space and append it at the line-start at subsequent non ...# lines (in the pattern space).

2

In bash:

#!/bin/bash

# grab one of the lines ending in #
prefix=$(egrep '#$' file | head -1)

cat file | while read line
do
    # if the line ends in a #, just print it
    if [[ $line =~ \#$ ]]
    then
        echo $line
    else
        # print the line prefixed with the $prefix
        printf -- "%s %s\n" "$prefix" "$line"
    fi
done

[[ $line =~ \#$ ]] is a regex if statement, same regex as in the egrep. If your shell doesn't like it, you can replace it with if egrep -q '#$' <<< line; then.

1
sed -e '
   /#$/!{
      G
      s/\(.*\)\n\(.*\)/\2\1/
      b
   }
   h
' 
1

Python solution:

#!/usr/bin/env python
import sys
prefix = ""
with open(sys.argv[1]) as fd:
    for line in fd:
        new_line = line.rstrip()
        if new_line.endswith('#'):
            prefix = new_line
        else:
            new_line = prefix + new_line
        print(new_line)

Test run:

$ ./append_word.py input.txt                                       
bbb-ccc-cccc#
bbb-ccc-cccc#   aasdf  asdas asdasa fgdg
bbb-ccc-cccc#   asdfa  asfdas  adfaq  asfa
bbb-ccc-cccc#   afdaf  fafa  fafd  afafa
bbb-ccc-cccc#

The way it works is simple. We read file line by line, stripping ending spaces or newline characters from the end. We record the prefix to each line if that line ends with # character. If the line doesn't end in #, we know that line needs to be modified. Finally, we print the line, regardless of whether it was modified or not.

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.