I have below table from my query, my separator is + however it is creating new lines under DESCRIPTION as shown in example below:

1+text1+HIGH+Aug 15
2+text+LOW+Aug 11
3+text+LOW+Aug 11
4+text1+HIGH+Aug 15

how will i make it looks like below using sed

1       text1 text2      HIGH     Aug 15
2       text             LOW      Aug 11
3       text             LOW      Aug 11
4       text text2 text3 HIGH     Aug 15

I have tried:

sed -n '1{h;n};/^ *+ */{s// /;H;n};{x;s/\n//g;p};${x;p}' 

but gives me error (sed: command garbled), thanks

  • 3
    Why sed particularly? Are you open to using other tools? May 1 '20 at 12:27
  • Shouldn't the 2nd column on the 4th row be "text1 text2 text3"? Also, can the output table be simplified to have only one character as the delimiter, say ','?
    – seshoumara
    May 1 '20 at 12:28
  • @seshoumara, yes you are correct, 4th row should be text1 text2 text3 on 2nd column and any delimiter will do, thanks
    – impitnin
    May 1 '20 at 12:31
  • Again: Can it be another tool, such as awk? It would give less headache. And do you have column utility?
    – Quasímodo
    May 1 '20 at 13:33
  • hi, yes any tool will do, appreciate it
    – impitnin
    May 1 '20 at 13:39
awk '
    {sub(/[[:blank:]]+$/,"")}  #Removes trailing blanks
    NF==4{print line;line=$0}
    NF==2{x=$2;$0=line;$2=$2" "x;line=$0}
    END{print line}
' file | column -ts "+"

The main idea behind it is that if NF==2, then put the second field $2 in the second field of the preceding line. So what happens is

1+text1+HIGH+Aug 15 #Puts this in "line" variable
+text2              #Transfer 2nd field to "line" second field
# Resulting in...
1+text1 text2+High+Aug 15

column removes the + from the output to generate this fine final output:

1     text1 text2        HIGH      Aug 15
2     text               LOW       Aug 11
3     text               LOW       Aug 11
4     text1 text2 text3  HIGH      Aug 15

Making a "field agnostic" awk

tac file | awk 'BEGIN{FS="+"}
    {flds=(NF>flds)?NF:flds;for (i=1; i<=NF; i++) {f=(length($i)>0)?gensub(/ +$/,"","g",$i)" ":"";fld[i]=f fld[i]}}
    $1>0{for (i=1; i<=flds; i++) printf fld[i]"+"; printf "\n"; delete fld}' | 
    tac | column -ts "+"

1      text1 text2         HIGH       Aug 15   
2      text                LOW        Aug 11   
3      text                LOW        Aug 11   
4      text1 text2 text3   HIGH       Aug 15  


Turn the file around so that you know it's time to print when $1 is not blank and set FS

tac file | awk 'BEGIN{FS="+"}

Keep count of the max no of flds, loop over the fields trimming them and checking for blanks, load them into an array by field to aggregate them

    {flds=(NF>flds)?NF:flds;for (i=1; i<=NF; i++) {f=(length($i)>0)?gensub(/ +$/,"","g",$i)" ":"";fld[i]=f fld[i]}}

If $1 is non-blank then output the line and reset the array

    $1>0{for (i=1; i<=flds; i++) printf fld[i]"+"; printf "\n"; delete fld}' | 

Pipe through tac to turn it around and use column to prettify it

    tac | column -ts "+"


Really didn't like the second loop, stopping to gensub every field when you can hit it with the sed hammer as it is piped and that the tests can be simplified. Felt inelegant.

So here is a golfed version that just feels better to me:

tac file | awk 'BEGIN{FS="+"}
        for (i=1; i<=NF; i++) {fld[i]=$i" "fld[i];
        if ($1) printf fld[i]"+"}}
    $1{ printf "\n"; delete fld}' |
    sed -E "s/  +/ /g" | tac | column -ts "+" 

Went back and read what differences may be between our versions of sed and re-wrote it from scratch. Hope this works:

        s:^\([^,]\{1,\},[^,]\{1,\}\)\([^\n]\{1,\}\)\n,\([^ \n]\{1,\}\).*$:\1 \3\2:


1,text1 text2,HIGH,Aug 15
2,text,LOW,Aug 11
3,text,LOW,Aug 11
4,text1 text2 text3,HIGH,Aug 15
  • My answer makes the assumption that the description texts do not contain space or plus. Let me know if this is not acceptable.
    – seshoumara
    May 1 '20 at 12:47
  • thanks, but it says , sed: illegal option -- r . I use SunOS
    – impitnin
    May 1 '20 at 12:48
  • no luck , both doesn't work for me :(
    – impitnin
    May 1 '20 at 12:56
  • Perhaps you don't have GNU sed, I'm on Ubuntu. Try this instead: sed -z ':l;s:^\([^+]\++[^+]\+\)\([^\n]\+\)\n+\([^ \n]\+\).*:\1 \3\2:m;tl;y:+:,:' input.txt.
    – seshoumara
    May 1 '20 at 13:00
  • yeah,i dont have GNU sed, the -z is illegal option too
    – impitnin
    May 1 '20 at 13:09
perl -0pe 'while(s/(.*?)\+(.*?)\+(.*)\n\+(.*)/$1+$2 $4+$3/g){}' file
  • perl -0 - slurp all the input
  • s/(.*?)\+(.*?)\+(.*)\n\+(.*)/$1+$2 $4+$3/g transforms A+B+...C\n+D in A+B D+...C while possible.

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.