Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have this input:

XX3083136|bla-bla texthere texttt|asfdasfd@safdsadf.com|SADFdsafsafd|ASDfasfdsafd|DSFSAFD|dsafasfd|sadfsad|
XX3083372|bla-bla-bla this is a text bla-bla|asdfsfda@lsadfasfdda.com|SDFsafas|SADFsa|DFSsdf|asdasdf|sadfdsafsaf|asdfsadf|

and I need this output:

XX3083136|bla-bla texthere textt|asfdasfd@safdsadf.com         |SADFdsafsafd|ASDfasfdsafd|DSFSAFD|dsafasfd|sadfsad|
XX3083372|bla-bla-bla this is a te|asdfsfda@lsadfasfdda.com      |SDFsafas|SADFsa|DFSsdf|asdasdf|sadfdsafsaf|asdfsadf|

So the difference is that I need to control the max text length between the "|". If it's shorter then given line lenght then " " need to be putted. In this example the second row is limited to max 24 chars and the third row has to be at least 30 chars. Different rows will need different max/min limits..

How can I do this in bash?

share|improve this question

2 Answers 2

up vote 2 down vote accepted

To supplement a little:

Basic format for printf in text is:

%s      # Print as is
%10     # Right justify minimum width print 10
%-10    # Left justify minimum width print 10
%.10    # Max width 10
%10.10  # Max width 10, min width print 10
%-10.10 # Left justify, max width 10, min width print 10
%*s     # Same as above, but get number from arguments
%-*s    # Same as above, but get number from arguments
...

When one get long patterns it can become a bit messy to keep track on where and which etc. One way to make it a bit easier could be something like this:

#!/bin/bash

usage()
{
    printf "Usage: %s <FILE>\n" "$(basename "$0")" >&2
    [[ -n "$1" ]] && echo "$1"
    exit 1
}

if [[ ! -t 0 ]]; then
    : # Piped to
elif [[ $# -eq 0 ]]; then
    usage "Missing file."
elif [[ ! -r "$1" ]]; then
    usage "Unable to read \`$1'."
else
    # Accept input from file to file descriptor 0, aka stdin
    exec 0< "$1"
fi

fmt=""
fmt="$fmt%s|"       # F1
fmt="$fmt%-24.24s|" # F2
fmt="$fmt%-30s|"    # F3
fmt="$fmt%-10.10s|" # F4
fmt="$fmt%-10.10s|" # F5
fmt="$fmt%-10s|"    # F6
fmt="$fmt%-2.2s|"   # F7
fmt="$fmt%-2.2s|\n" # F8

# Set IFS to newline and bar and read fields
# assigning them to f1 ... f8
#
while IFS=$'\n'"|" read f1 f2 f3 f4 f5 f6 f7 f8; do
    printf "$fmt"\
    "$f1" "$f2" "$f3" "$f4" \
    "$f5" "$f6" "$f7" "$f8"
done <&0

Or. e.g.:

while IFS=$'\n'"|" read f1 f2 f3 f4 f5 f6 f7 f8; do
    printf "%s|%-*.*s|%-*s\n" \
    "$f1" \
    24 24 "$f2" \
    30 "$f3"
done < "input"

If it is only line parsing then awk is an good alternative. e.g.:

#!/usr/bin/awk -f

BEGIN {FS="|"}
/^/ {
    printf "%s|%-24.24s|%-30s|%-10.10s|%-10.10s|%-10s\n",
        $1, $2, $3, $4, $5, $6
}

Or:

#!/usr/bin/awk -f

BEGIN {
    FS="|"
}
/^/ {
    printf "%s|%-*.*s|%-*s|%-*.*s|%-*.*s|%-*s\n",
        $1,
        24, 24, $2,
        30, $3,
        10, 10, $4,
        10, 10, $5,
        10, $6
}
share|improve this answer

You can play around printf builtin to cut a string :

$ printf '%.2s\n' 'azerty'
az

And using it to put the delimiter | where you want, ex :

$ printf '%10s%10s%10s%10s%10s%10s\n' '|' '|' '|' '|' '|' '|'
         |         |         |         |         |         |

See http://www.bash-hackers.org/wiki/doku.php/commands/builtin/printf

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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