0

I have the following entry in a tab delimited file test1.txt

key1 10|15 20 30 11|xx 21 31
key2 527|240 269 0 0 0 2462 546 281 0 0

Is there any way I can transform this file into the below format? I would like bring pipe delimited values into next line. There can be an arbitrary amount of pipes and values which are separated by them must have the same amount of characters.

key1 10 20 30 11 21 31
key1 15       xx    
key2 527 269 0 0 0 2462 546 281 0 0
key2 240  
  • 7
    If you want precise positioning (the same character columnts), it won't be a tab delimited data anymore. What output do you expect for input lines of varying length (1 or 3+ digits per number)? – peterph Oct 23 '14 at 19:59
1

My script works, if both your values separated by the delimiter have the same amount of characters.

If you wanted arbitrary number of spaces, it wouldn't be generally possible, because you could get into situations like this one:

key1 12|1222 10|15

key1 12 10
key1 1222 15

In this situation you simply cannot align '15' into the column under '10', because the column is already occupied by '1222'.

EDIT: I've rewritten the code, so it takes an arbitrary number of lines and even an arbitrary number of pipes too :-)


CODE:

script.sh:

#!/bin/bash

# count max number of pipes -> recognizes number of lines
countPipes() {
    num=0
    line="$@"
    IFS=' '
    for item in $line; do
        tmp=$(echo "$item" | tr -d "[:alnum:][:space:]" | wc -c)

        if [[ "$num" < "$tmp" ]]; then
            num=$tmp
        fi
    done

    return $num
}

makeLines() {
    strTmp="$@" # one line from input file

    arrTmp=($strTmp)
    key=${arrTmp[0]}
    arrIN=$key

    # create output arrays (one array = one output line)
    countPipes $strTmp
    maxPipeCount=$? # assign the value of the last 'return'

    for((i=0;i<$maxPipeCount;++i)); do
        arrOUT[$i]="$key"
    done

    for item in ${strTmp[@]}; do
        # Delimiter handling
        if [[ $item == *\|* ]]; then # If there is a pipe
            IFS='|'
            tmp=($item) # variable containing pipe character -> split by space -> array
            IFS=' '

            arrIN="$arrIN ${tmp[0]}"

            for ((i=0;i<"${#arrOUT[@]}";++i)); do # fill columns in every output line - i = index in line
                if [[ "${#tmp[$(($i + 1))]}" -gt 0 ]]; then
                    arrOUT[$i]="${arrOUT[$i]} ${tmp[$(($i + 1))]}"
                else
                    # Handling spaces in output
                    for ((j=0;j<="${#tmp[0]}";++j)); do # filling line with spaces - j = just counter, not index
                        arrOUT[$i]="${arrOUT[$i]} "
                    done
                fi
            done

        elif [[ "$item" != "$key" ]]; then # If there isn't a pipe
            arrIN="$arrIN $item"

            # Handling spaces in output
            for ((i=0;i<"${#arrOUT[@]}";++i)); do # for every output line
                for j in { 1.."${#tmp[0]}" }; do # for every char in $item
                    arrOUT[$i]="${arrOUT[$i]} "
                done
            done
        fi
    done

    # PRINT RESULTS
    echo "$arrIN"

    for((i=0;i<"${#arrOUT[@]}";++i)); do # for every output line
        echo "${arrOUT[$i]}"
    done
    unset arrOUT

    echo '-----------------------------------------------------------'
}

while read line; do # load data from STDIN
    makeLines $line
done

Example:

test.txt:

key1 10|15 20 30 11|XX|55 21 31|20 100
key2 11 25|30 58|22 44 33
key3 12|15|17|19 22 33 55|22 88|44|11 xxxx|2222|7777

Command:

bash ./script.sh < test.txt 

OUTPUT:

key1 10 20 30 11 21 31 100
key1 15       XX    20   
key1          55         
-----------------------------------------------------------
key2 11 25 58 44 33
key2    30 22      
-----------------------------------------------------------
key3 12 22 33 55 88 xxxx
key3 15       22 44 2222
key3 17          11 7777
key3 19                 
-----------------------------------------------------------
  • oh yeah, I made this program only for oneline input, as I understood from your question. What is the result supposed to look like in this more complicated case? – Eenoku Oct 23 '14 at 23:08
  • So, your ideal output consists of changing lines with 'first' or 'second' values according to a pipe? – Eenoku Oct 23 '14 at 23:13
  • can we accomplish it using awk or sed ? coz, it has the capability to add new lines and also handle the structured data? – janani Oct 23 '14 at 23:54
  • I'm not sure about awk or sed, to be honest, I don't use them very much. – Eenoku Oct 24 '14 at 5:22
  • @janani I've finally added the complete solution – Eenoku Oct 26 '14 at 23:26

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.