如何在 Bash 中对表的列?

我想将文本格式化为表格。我尝试使用 '\t'分隔符进行回显,但它没有对齐。

预期输出:

a very long string..........     112232432      anotherfield
a smaller string                 123124343      anotherfield
149500 次浏览

Use the column command:

column -t -s' ' filename

To have the exact same output as you need, you need to format the file like this:

a very long string..........\t     112232432\t     anotherfield\n
a smaller string\t      123124343\t     anotherfield\n

然后使用:

$ column -t -s $'\t' FILE
a very long string..........  112232432  anotherfield
a smaller string              123124343  anotherfield

printf is great, but people forget about it.

$ for num in 1 10 100 1000 10000 100000 1000000; do printf "%10s %s\n" $num "foobar"; done
1 foobar
10 foobar
100 foobar
1000 foobar
10000 foobar
100000 foobar
1000000 foobar


$ for((i=0;i<array_size;i++));
do
printf "%10s %10d %10s" stringarray[$i] numberarray[$i] anotherfieldarray[%i]
done

注意,我对字符串使用了 %10s%s是重要的组成部分。它告诉它使用一个字符串。中间的 10表示要有多少列。%d是数字(数位)。

有关更多信息,请参见 man 1 printf

我不确定您在哪里运行它,但是您发布的代码不会产生您给出的输出,至少在我熟悉的 Bash 版本中不会。

试试这个:

stringarray=('test' 'some thing' 'very long long long string' 'blah')
numberarray=(1 22 7777 8888888888)
anotherfieldarray=('other' 'mixed' 456 'data')
array_size=4


for((i=0;i<array_size;i++))
do
echo ${stringarray[$i]} $'\x1d' ${numberarray[$i]} $'\x1d' ${anotherfieldarray[$i]}
done | column -t -s$'\x1d'

注意,我使用的是 群分隔字符群分隔字符(0x1D)而不是 tab,因为如果您从文件中获取这些数组,它们可能包含 tab。

比你想象的要简单。

如果您也使用分号分隔的文件和头:

$ (head -n1 file.csv && sort file.csv | grep -v <header>) | column -s";" -t

如果使用数组(使用制表符作为分隔符) :

for((i=0;i<array_size;i++));
do


echo stringarray[$i] $'\t' numberarray[$i] $'\t' anotherfieldarray[$i] >> tmp_file.csv


done;


cat file.csv | column -t
function printTable()
{
local -r delimiter="${1}"
local -r data="$(removeEmptyLines "${2}")"


if [[ "${delimiter}" != '' && "$(isEmptyString "${data}")" = 'false' ]]
then
local -r numberOfLines="$(wc -l <<< "${data}")"


if [[ "${numberOfLines}" -gt '0' ]]
then
local table=''
local i=1


for ((i = 1; i <= "${numberOfLines}"; i = i + 1))
do
local line=''
line="$(sed "${i}q;d" <<< "${data}")"


local numberOfColumns='0'
numberOfColumns="$(awk -F "${delimiter}" '{print NF}' <<< "${line}")"


# Add Line Delimiter


if [[ "${i}" -eq '1' ]]
then
table="${table}$(printf '%s#+' "$(repeatString '#+' "${numberOfColumns}")")"
fi


# Add Header Or Body


table="${table}\n"


local j=1


for ((j = 1; j <= "${numberOfColumns}"; j = j + 1))
do
table="${table}$(printf '#| %s' "$(cut -d "${delimiter}" -f "${j}" <<< "${line}")")"
done


table="${table}#|\n"


# Add Line Delimiter


if [[ "${i}" -eq '1' ]] || [[ "${numberOfLines}" -gt '1' && "${i}" -eq "${numberOfLines}" ]]
then
table="${table}$(printf '%s#+' "$(repeatString '#+' "${numberOfColumns}")")"
fi
done


if [[ "$(isEmptyString "${table}")" = 'false' ]]
then
echo -e "${table}" | column -s '#' -t | awk '/^\+/{gsub(" ", "-", $0)}1'
fi
fi
fi
}


function removeEmptyLines()
{
local -r content="${1}"


echo -e "${content}" | sed '/^\s*$/d'
}


function repeatString()
{
local -r string="${1}"
local -r numberToRepeat="${2}"


if [[ "${string}" != '' && "${numberToRepeat}" =~ ^[1-9][0-9]*$ ]]
then
local -r result="$(printf "%${numberToRepeat}s")"
echo -e "${result// /${string}}"
fi
}


function isEmptyString()
{
local -r string="${1}"


if [[ "$(trimString "${string}")" = '' ]]
then
echo 'true' && return 0
fi


echo 'false' && return 1
}


function trimString()
{
local -r string="${1}"


sed 's,^[[:blank:]]*,,' <<< "${string}" | sed 's,[[:blank:]]*$,,'
}

样本运行

$ cat data-1.txt
HEADER 1,HEADER 2,HEADER 3


$ printTable ',' "$(cat data-1.txt)"
+-----------+-----------+-----------+
| HEADER 1  | HEADER 2  | HEADER 3  |
+-----------+-----------+-----------+


$ cat data-2.txt
HEADER 1,HEADER 2,HEADER 3
data 1,data 2,data 3


$ printTable ',' "$(cat data-2.txt)"
+-----------+-----------+-----------+
| HEADER 1  | HEADER 2  | HEADER 3  |
+-----------+-----------+-----------+
| data 1    | data 2    | data 3    |
+-----------+-----------+-----------+


$ cat data-3.txt
HEADER 1,HEADER 2,HEADER 3
data 1,data 2,data 3
data 4,data 5,data 6


$ printTable ',' "$(cat data-3.txt)"
+-----------+-----------+-----------+
| HEADER 1  | HEADER 2  | HEADER 3  |
+-----------+-----------+-----------+
| data 1    | data 2    | data 3    |
| data 4    | data 5    | data 6    |
+-----------+-----------+-----------+


$ cat data-4.txt
HEADER
data


$ printTable ',' "$(cat data-4.txt)"
+---------+
| HEADER  |
+---------+
| data    |
+---------+


$ cat data-5.txt
HEADER


data 1


data 2


$ printTable ',' "$(cat data-5.txt)"
+---------+
| HEADER  |
+---------+
| data 1  |
| data 2  |
+---------+

参考 LIB at: https://github.com/gdbtek/linux-cookbooks/blob/master/libraries/util.bash

处理 stdin 的 awk解决方案

Since column is not POSIX, maybe this is:

mycolumn() (
file="${1:--}"
if [ "$file" = - ]; then
file="$(mktemp)"
cat > "${file}"
fi
awk '
FNR == 1 { if (NR == FNR) next }
NR == FNR {
for (i = 1; i <= NF; i++) {
l = length($i)
if (w[i] < l)
w[i] = l
}
next
}
{
for (i = 1; i <= NF; i++)
printf "%*s", w[i] + (i > 1 ? 1 : 0), $i
print ""
}
' "$file" "$file"
if [ "$1" = - ]; then
rm "$file"
fi
)

Test:

printf '12 1234 1
12345678 1 123
1234 123456 123456
' > file

测试命令:

mycolumn file
mycolumn <file
mycolumn - <file

所有人的产出:

      12   1234      1
12345678      1    123
1234 123456 123456

参见:

为了以防有人想在 PHP 中这样做,我在 GitHub 上贴出了一个要点:

Https://gist.github.com/redestructa/2a7691e7f3ae69ec5161220c99e2d1b3

Simply call:

$output = $tablePrinter->printLinesIntoArray($items, ['title', 'chilProp2']);

如果使用的是7.2以上的 PHP 版本,可能需要对代码进行调整。

After that, call echo or writeLine depending on your environment.

下面的代码已经经过测试,并且完全符合原始问题中的要求。

参数:

% 30s 30字符列和文本右对齐。
% 10d 整数表示法,% 10s 也可以使用

stringarray[0]="a very long string.........."
# 28Char (max length for this column)
numberarray[0]=1122324333
# 10digits (max length for this column)
anotherfield[0]="anotherfield"
# 12Char (max length for this column)
stringarray[1]="a smaller string....."
numberarray[1]=123124343
anotherfield[1]="anotherfield"


printf "%30s %10d %13s" "${stringarray[0]}" ${numberarray[0]} "${anotherfield[0]}"
printf "\n"
printf "%30s %10d %13s" "${stringarray[1]}" ${numberarray[1]} "${anotherfield[1]}"
# a var string with spaces has to be quoted
printf "\n Next line will fail \n"
printf "%30s %10d %13s" ${stringarray[0]} ${numberarray[0]} "${anotherfield[0]}"






a very long string.......... 1122324333  anotherfield
a smaller string.....  123124343  anotherfield

当一行以分隔符开头或者有两个或多个连续的分隔符时,column -t会跳过空字段:

$ printf %s\\n a,b,c a,,c ,b,c|column -s, -t
a   b  c
a   c
b   c

因此,我改用 awk 函数(它需要 gawk,因为它使用数组的数组) :

$ tab(){ awk '{if(NF>m)m=NF;for(i=1;i<=NF;i++){a[NR][i]=$i;l=length($i);if(l>b[i])b[i]=l}}END{for(h in a){for(i=1;i<=m;i++)printf("%-"(b[i]+n)"s",a[h][i]);print""}}' n="${2-1}" "${1+FS=$1}"|sed 's/ *$//';}
$ printf %s\\n a,b,c a,,c ,b,c|tab ,
a b c
a   c
b c

if you data doesn't contain the equal sign ("=") anywhere in it, you can use that as a shell-friendly delimiter for column without having to escape anything -

  • 通过将 FS修改为一个制表符("\t")加上任意数量的空格(" ")或制表符("\t") ,或者一个2个或更多空格的连续块,它还允许输入数据在每个字段中有任意数量的单个空格

     echo "${inputdata2}" |
    
 mawk NF=NF OFS== FS=' + |[ \t]*\t[ \t]*' |
 

column -s= -t
a very long string..........  112232432  anotherfield
a smaller string              123124343  anotherfield

如果数据确实包含等号,使用一个在典型数据中几乎不可能存在的组合 sep:

gawk -e NF=NF OFS='\301\372\5' FS=' + |[ \t]*\t[ \t]*' |


LC_ALL=C column -s$'\301\372\5' -t
a very long string..........  112232432  anotherfield
a smaller string              123124343  anotherfield

如果你的数据只有2列,并且你大概知道第一个字段有多宽,你可以使用这个 \r技巧在屏幕上进行优秀的格式化(但是如果你需要把它发送到下一个管道,它们不会变成空格) :

# each \t is 8-spaces at console terminal


mawk NF=2 FS=' + |[ \t]*\t[ \t]*' OFS='\r\t\t\t\t'
a very long string..........    112232432
a smaller string                123124343