使用bash工具很好地格式化字符串中的列

时间:2014-07-21 18:16:29

标签: string shell multiple-columns tabstop

我们说我有一个文件orders.txt,其中包含以下内容:

#   Description Amount  Price   Sum
1   Beermat 1000    0,01€   10€
2   Glass   100 1€  100€
3   Long description    1   10€ 10€
4   An even longer description  1   10€ 10€
5   An extra long description, for real!    1   10€ 10€
6   An extra long description, almost max. length   1   10€ 10€
7   Long description for some really fancy product and unfortunately this description is too long to fit into one line - bad luck!  1   10€ 10€
8   This line isn’t shown afterwards    1   1€  1€

用tabstop a.k.a. \t

分隔列

通常我使用一个小工具column -ts $'\t' order.txt来格式化这些东西,这会导致类似:

#  Description                                    Amount  Price  Sum
1  Beermat                                        1000    0,01€  10€
2  Glass                                          100     1€     100€
3  Long description                               1       10€    10€
4  An even longer description                     1       10€    10€
5  An extra long description, for real!           1       10€    10€
6  An extra long description, almost max. length  1       10€    10€

只要一行不超过终端窗口的线宽,这样就可以正常工作。因此,对于第7行,此工具会输出column: line too long并退出。

我正在寻找的是一种解决方案,可以为我生成如下输出:

#  Description                                    Amount  Price  Sum
1  Beermat                                        1000    0,01€  10€
2  Glass                                          100     1€     100€
3  Long description                               1       10€    10€
4  An even longer description                     1       10€    10€
5  An extra long description, for real!           1       10€    10€
6  An extra long description, almost max. length  1       10€    10€
7  Long description for some really fancy product 1       10€    10€
   and unfortunately this description is too long
   to fit into one line - bad luck!
8  This line isn’t shown afterwards               1       1€     1€

1 个答案:

答案 0 :(得分:2)

处理长描述并在固定宽度后打印金额,价格和总和,同时拆分字符串以在金额,价格和汇总行之后打印剩余部分并非无足轻重。分割字符串的方法不止一种,而且更多的方法更优雅,但是一个蛮力的例子会给你一个想法。你可以按照自己的意愿整理一下。只需通过更改dwidth变量或在文件名后面提供所需宽度作为第二个参数来设置宽度。

这假设您的输入格式与您所描述的相同,字段以tabs分隔,例如:#\tDescription\tAmount\tPrice\tSum

#!/bin/bash

test -r "$1" || { 
    printf "Error: insufficient input, usage ${0//*\//} <orders file>\n\n"
    exit 1
}

oifs=$IFS           # set IFS to only break on tab or newline
IFS=$'\t\n'

dwidth=${2:-50}     # set the print width you want for description (default 50)
i=0

while read num desc amt price sum || test -n "$num"; do

    # test description > width, if so print only first 50 (or on word break)
    if test "${#desc}" -ge "$dwidth" ; then
        for ((i=$dwidth; i>0; i--)); do
            test "${desc:$i:1}" = ' ' && break
        done

        end=$i
        printf "%2s %-*s %-8s %-8s %-8s\n" $num $dwidth "${desc:0:end}" $amt $price $sum

        remain=$((${#desc}-$end))       # calculate remaining chars to print

        while test "$remain" -gt 0; do  # while characters remain
            strt=$((end+1))             # start printing at last end
            if test "$remain" -gt "$dwidth"; then   # test if more than width remain
                for ((i=$dwidth; i>0; i--)); do     # if so, break on word
                    test "${desc:$((strt+i)):1}" = ' ' && break
                done
                end=$((strt+i))         # set end equal to start + chars in words
                printf "   %-*s\n" $dwidth "${desc:$strt:$i}"   # print to width
            else
                printf "   %-*s\n" $dwidth "${desc:$strt}"      # print rest and break
                break
            fi
            remain=$((${#desc}-$end))   # calculate new remaining chars
        done
    else    # if description not > width, just print it
        printf "%2s %-*s %-8s %-8s %-8s\n" $num $dwidth $desc $amt $price $sum
    fi

done < "$1"

exit 0

输出:$ bash orders.sh orders.txt

# Description                                        Amount   Price    Sum
1 Beermat                                            1000     0,01€    10€
2 Glass                                              100      1€       100€
3 Long description                                   1        10€      10€
4 An even longer description                         1        10€      10€
5 An extra long description, for real!               1        10€      10€
6 An extra long description, almost max. length      1        10€      10€
7 Long description for some really fancy product and 1        10€      10€
  unfortunately this description is too long to fit
  into one line - bad luck!
8 This line isn’t shown afterwards                   1        1€       1€

输出:$ bash orders.sh orders.txt 60

# Description                                                  Amount   Price    Sum
1 Beermat                                                      1000     0,01€    10€
2 Glass                                                        100      1€       100€
3 Long description                                             1        10€      10€
4 An even longer description                                   1        10€      10€
5 An extra long description, for real!                         1        10€      10€
6 An extra long description, almost max. length                1        10€      10€
7 Long description for some really fancy product and           1        10€      10€
  unfortunately this description is too long to fit into one
  line - bad luck!
8 This line isn't shown afterwards                             1        1€       1€