如何在 Bash 中对齐表格的列?
Posted
技术标签:
【中文标题】如何在 Bash 中对齐表格的列?【英文标题】:How can I align the columns of tables in Bash? 【发布时间】:2012-09-27 22:43:54 【问题描述】:我想输出一个表格格式的文本。我试图做的是用 '\t' 回显数组的元素,但它没有对齐。
我的代码
for((i=0;i<array_size;i++));
do
echo stringarray[$i] $'\t' numberarray[$i] $'\t' anotherfieldarray[$i]
done;
我的输出
a very long string.......... 112232432 anotherfield
a smaller string 123124343 anotherfield
期望的输出
a very long string.......... 112232432 anotherfield
a smaller string 123124343 anotherfield
【问题讨论】:
【参考方案1】:使用 column 命令:
column -t -s' ' filename
【讨论】:
这不适用于问题中给出的示例,因为第一列数据中有空格。 @BurhanAli 我需要重复我之前的评论吗?所有答案都假设有一些分隔符。 OP没有说分隔符。所以同样的分隔符也可以在列中使用。 因为第一列数据有空格那你怎么称呼它为第一列呢? 无需重复。我读了他们。我的评论基于问题中所需的输出。在给定的输入上使用此答案不会产生所需的输出。 准备分隔符的例子:cat /etc/fstab | sed -r 's/\s+/ /g' | column -t -s' '
准备分隔符的例子:sed -r 's/\s+/ /g' /etc/fstab | column -t -s' '
【参考方案2】:
以下代码已经过测试,完全符合原始问题的要求。
参数:
%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
【讨论】:
正如上面@steffen 所指出的,左对齐使用“-”符号,即 printf "%-30s " "$stringarray[0]"【参考方案3】:以防万一有人想在 php 中这样做,我在 GitHub 上发布了一个要点:
https://gist.github.com/redestructa/2a7691e7f3ae69ec5161220c99e2d1b3
只需调用:
$output = $tablePrinter->printLinesIntoArray($items, ['title', 'chilProp2']);
如果您使用的是早于 7.2 的 PHP 版本,您可能需要调整代码。
之后,根据您的环境调用 echo 或 writeLine。
【讨论】:
【参考方案4】:这比你想象的要容易。
如果您也在使用以分号分隔的文件和标题:
$ (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
【讨论】:
【参考方案5】:我不确定你在哪里运行它,但你发布的代码不会产生你给出的输出,至少在我熟悉的 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'
请注意,我使用的是group separator character (0x1D) 而不是制表符,因为如果您从文件中获取这些数组,它们可能包含制表符。
【讨论】:
【参考方案6】:要获得与您需要的完全相同的输出,您需要像这样格式化文件:
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
【讨论】:
$'\t'
中的$
在做什么?
如果 2 列的大小不同超过大约 5 个字符,则使用制表位将完全无法使用。
@rjmunro "Words of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard."【参考方案7】:
awk
处理标准输入的解决方案
由于column
不是 POSIX,也许这是:
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
)
测试:
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
另见:
Using awk to align columns in text file? AWK: go through the file twice, doing different tasks【讨论】:
末尾的if [ "$file" = - ]; then
应该是if [ "$1" = - ]; then
。使用当前代码,您永远不会清理临时文件。【参考方案8】:
printf
很棒,但人们忘记了它。
$ 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
了解更多信息。
【讨论】:
只有一个在打印表格时有用的建议:%-10s
将生成长度为 10 的左对齐字符串
@UtahJarhead 对变量 stringarray[$i] 的引用应替换为 $stringarray[i] 并具有必须引用的第一个字符串空格 "$stringarray[i]"避免空格字符被解释为分隔符。【参考方案9】:
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 "$iq;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 "%$numberToRepeats")"
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 |
+---------+
参考库:https://github.com/gdbtek/linux-cookbooks/blob/master/libraries/util.bash
【讨论】:
谢谢!这正是我想要的。对于 mac 用户:您必须删除 echo 命令中的-e
参数才能正确打印破折号。
很棒的样品!谢谢!
更改输出颜色确实会影响对齐。不知道为什么......嗯
@mattdevio 你找到颜色的修复了吗?
注意!参考链接有更新的功能。以上是关于如何在 Bash 中对齐表格的列?的主要内容,如果未能解决你的问题,请参考以下文章