5

Input:

ABC,SSSD,12345,NSS,12345,xxx,TS11
,,,,,,TS21
,,,,,,TS22
,,,,,,BS26
,,,,,,GPRS
ABC,SSSD,12356,NSS,12356,xxx,TS11
,,,,,,TS21
,,,,,,TS22
,,,,,,GPRS
,,,,,,BS26

Output:

ABC,SSSD,12345,NSS,12345,xxx,TS11|TS21|TS22|BS26|GPRS
ABC,SSSD,12356,NSS,12356,xxx,TS11|TS21|TS22|GPRS|BS26

I have already tried by writing the below script. But it is performance intensive:

#!/bin/bash
head -2 $1 | tail -1 >> $1"_output"
sed '1,2d' $1 > temp$1.txt
cp temp$1.txt $1
rm -f temp$1.txt
while IFS='' read -r line || [[ -n $line ]]; do
    #    echo "main line -- "$line
    #    echo "prev line -- "$prevLine
    service=`echo $line | cut -d "," -f1`
    value=`echo $line | cut -d "," -f7`
    if [ "$service" != "" ]
    then
        if [ "$prevLine" != "" -a "$mvalue" != "" ]
        then
            echo $prevLine$mvalue >> $1"_output"
        fi
        prevLine=$line
        mvalue=""
    else
        #echo $value
        mvalue=$mvalue"|"$value
        #echo $mvalue
    fi
done < "$1"
echo $prevLine$mvalue >> $1"_output"

Can somebody suggests a better way to do this using awk or any better method?

1
  • 1
    Is the line ,,,,,,TS21 is as it is shown or is it ABC,SSSD,12345,NSS,12345,xxx,TS21 ?
    – amisax
    Aug 19 '15 at 7:17
5

If you are not too fussy about white space, you do it with two awk rules like this:

 awk '$1 { printf "\n" $0 } !$1 { printf OFS $7 }' FS=, OFS='|' infile

This leaves an empty line at the head of the output, and misses one at the end. To fix this add if and END clauses:

awk '$1 { if(NR>1) printf "\n"; printf $0 } !$1 { printf OFS $7 } END { printf "\n" }' FS=, OFS='|' infile

Output:

ABC,SSSD,12345,NSS,12345,xxx,TS11|TS21|TS22|BS26|GPRS
ABC,SSSD,12356,NSS,12356,xxx,TS11|TS21|TS22|GPRS|BS26

Edit - note about % in $0

As Glenn noted in the comments, if your lines contain %, it is safer to call printf with a format string, e.g.:

awk '$1 { if(NR>1) printf "\n"; printf "%s", $0 } !$1 { printf OFS"%s", $7 } END { printf "\n" }' FS=, OFS='|' infile
2
  • It's safer to use printf "\n%s", $0 -- in case the input line contains % characters. Aug 19 '15 at 12:37
  • @glennjackman: You're right, I made an edit with a warning. Thanks.
    – Thor
    Aug 20 '15 at 7:22
3

As long as the amount of lines to merge is constant awk simply can decide what a type of line it is by looking at the line number:

$ awk -F, '1==NR%5 { printf $0 "|";next } { printf $7 (NR%5?"|":"\n") }' DATA
ABC,SSSD,12345,NSS,12345,xxx,TS11|TS21|TS22|BS26|GPRS
ABC,SSSD,12356,NSS,12356,xxx,TS11|TS21|TS22|GPRS|BS26
2

Other sed:

sed ':;N;s/\n,,,*/|/;t;P;D' file
3
  • Should use a tag for t, but otherwise nice answer :) I always forget about P and D
    – 123
    Aug 19 '15 at 12:08
  • @User112638726 No, should not. If there is 1 point only tag can be miss.
    – Costas
    Aug 19 '15 at 17:03
  • It's not supposed to work that way, it just works by accident, GNU sed doesn't check for an empty label on ":" and upon t/b looks in the list of defined labels first before considering an empty label as the special case it's meant to be. It is not documented to work that way. It may not work that way in future versions. – Stéphane Chazelas
    – 123
    Aug 21 '15 at 7:58
2

Similar answer to Thor's, but stores the line until it's complete:

awk -F, '
    $1 != "" {if (record) print record; record = $0} 
    $1 == "" {record = record "|" $NF} 
    END {if (record) print record}
' file

If you want to use bash, then you can do

lines=()
while IFS= read -r line; do 
    if [[ ${line:0:1} == "," ]]; then   # the line starts a comma
        lines[-1]+="|${line##*,}"       # strip the commas and append to
                                        # the last element of the array
    else
        lines+=("$line")                # append the line to the array
    fi
done <<"$1"
printf "%s\n" "${lines[@]}"
ABC,SSSD,12345,NSS,12345,xxx,TS11|TS21|TS22|BS26|GPRS
ABC,SSSD,12356,NSS,12356,xxx,TS11|TS21|TS22|GPRS|BS26
1

if it its always the same number of rows (5), a combination of paste and sed can do:

paste -d',' - - - - - <file | sed 's/,\{2,\}/|/g'

will 1) paste 5 rows into one using , as delimiter, 2) replace multiple (2 or more) commas by one pipe

2
  • This will only work with the exact number of multiple comma lines shown in the question, which i would assume is not standard or OP could have asked how to merge 5 lines.
    – 123
    Aug 19 '15 at 8:55
  • Well, that's why I mentioned it. But the example input somehow let's me assume it is actually a fixed number of rows. OP might not have been aware that this simplified his request.
    – FelixJN
    Aug 19 '15 at 8:59

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.