Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Use printf to align columns.

320 views
Skip to first unread message

Hongyi Zhao

unread,
Aug 26, 2020, 7:10:46 AM8/26/20
to
Hi,

I use the following two lines in my bash script to align the output on terminal:

printf "%s\t\t%s\n" "18888(socks5)" "18889(socks5-sticky-session)"
18888(socks5) 18889(socks5-sticky-session)
printf "%s\t%s\n" "18886(stats,http)" "18887(socks5-health-check)"
18886(stats,http) 18887(socks5-health-check)


I found that I must use two \t in the first printf command for aligning them.

Any hints?

Jorgen Grahn

unread,
Aug 26, 2020, 7:46:39 AM8/26/20
to
Tabs are no good for aligning columns, as you saw. Use the widths in
printf, e.g. %20s or $-20s.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Kenny McCormack

unread,
Aug 26, 2020, 8:32:11 AM8/26/20
to
In article <7aa5b728-cf98-4262...@googlegroups.com>,
Hongyi Zhao <hongy...@gmail.com> wrote:
>Hi,
>
>I use the following two lines in my bash script to align the output on
>the terminal:
>
>printf "%s\t\t%s\n" "18888(socks5)" "18889(socks5-sticky-session)"
>18888(socks5) 18889(socks5-sticky-session)
>printf "%s\t%s\n" "18886(stats,http)" "18887(socks5-health-check)"
>18886(stats,http) 18887(socks5-health-check)
>
>
>I found that I must use two \t in the first printf command for aligning them.

You can, of course, just ditch using tabs and use hard spaces. That's the
easy way.

But I like what you are doing - I do it myself in my code. I like tabs (I
won't go into the reasons here).

Note: I did this thing first in AWK and it is a little easier to handle in
AWK, but here is the shell version:

#!/bin/bash
do_print() {
local str=$(printf "%*s" $((3-(${#1}+1)/8)) "")
printf "%s%s" "$1" "${str// /$'\t'}"
}
do_print "18888(socks5)";echo "18889(socks5-sticky-session)"
do_print "18886(stats,http)";echo "18887(socks5-health-check)"

--
You are again heaping damnation upon your own head by your statements.

- Rick C Hodgin -

William Ahern

unread,
Aug 27, 2020, 12:30:10 AM8/27/20
to
FWIW, the BSD utility column(1) can align input records into columns.
(Included with bsdmainutils package on Debian/Ubuntu systems.) It buffers
the input to determine the column widths. Use the -t (table) option if
you're just trying to justify fields in each input line.

If you can buffer the data and calculate the column widths in regular shell
code (e.g. `while F1 F2; do [ "${#F1}" -gt "$N1" ] && N1="${#F1}" ... done`
loop), you could pass the widths (offset by one column) as the -t option to
the POSIX expand(1) utility to emulate `column -t`. Of course, if you can
loop over the lines & fields once you could do it twice, using the printf
field width specifier as mentioned elsethread, avoiding expand(1).

hongy...@gmail.com

unread,
Aug 27, 2020, 4:18:29 AM8/27/20
to
在 2020年8月26日星期三 UTC+8 下午8:32:11,<Kenny McCormack> 写道:
> In article <7aa5b728-cf98-4262...@googlegroups.com>,
> Hongyi Zhao <hongy...@gmail.com> wrote:
> >Hi,
> >
> >I use the following two lines in my bash script to align the output on
> >the terminal:
> >
> >printf "%s\t\t%s\n" "18888(socks5)" "18889(socks5-sticky-session)"
> >18888(socks5) 18889(socks5-sticky-session)
> >printf "%s\t%s\n" "18886(stats,http)" "18887(socks5-health-check)"
> >18886(stats,http) 18887(socks5-health-check)
> >
> >
> >I found that I must use two \t in the first printf command for aligning them.
> You can, of course, just ditch using tabs and use hard spaces. That's the
> easy way.
>
> But I like what you are doing - I do it myself in my code. I like tabs (I
> won't go into the reasons here).
>
> Note: I did this thing first in AWK and it is a little easier to handle in
> AWK,

What's the awk code used by you for this job? But this will make you mix the shell code with awk in your script.

hongy...@gmail.com

unread,
Aug 27, 2020, 4:29:21 AM8/27/20
to
在 2020年8月26日星期三 UTC+8 下午8:32:11,<Kenny McCormack> 写道:
> In article <7aa5b728-cf98-4262...@googlegroups.com>,
> Hongyi Zhao <hongy...@gmail.com> wrote:
> >Hi,
> >
> >I use the following two lines in my bash script to align the output on
> >the terminal:
> >
> >printf "%s\t\t%s\n" "18888(socks5)" "18889(socks5-sticky-session)"
> >18888(socks5) 18889(socks5-sticky-session)
> >printf "%s\t%s\n" "18886(stats,http)" "18887(socks5-health-check)"
> >18886(stats,http) 18887(socks5-health-check)
> >
> >
> >I found that I must use two \t in the first printf command for aligning them.
> You can, of course, just ditch using tabs and use hard spaces. That's the
> easy way.
>
> But I like what you are doing - I do it myself in my code. I like tabs (I
> won't go into the reasons here).
>
> Note: I did this thing first in AWK and it is a little easier to handle in
> AWK, but here is the shell version:
>
> #!/bin/bash
> do_print() {
> local str=$(printf "%*s" $((3-(${#1}+1)/8)) "")
> printf "%s%s" "$1" "${str// /$'\t'}"

It seems so complicated and I'm still not so clear on the logic of the above two lines. Any hints?

Best regards,
HY

Kenny McCormack

unread,
Aug 27, 2020, 11:14:07 AM8/27/20
to
In article <l0pj1h...@wilbur.25thandClement.com>,
Hmmm. column is another one of those "forgotten Unix utilities". Could be
interesting. Of course:

1) It doesn't do tabs - which seemed to be what OP was interested in.
2) It would (probably) require a complete re-design/re-write of
whatever OP is working on.

Note that #1 could be addressed by using "unexpand" to convert it back to
tabs. I spent a few minutes messing around with "column" and "unexpand",
but didn't really achieve much.

Still, if OP could re-design his script so that everything gets written to
a file, and then at the end, push that file through "column -t", it might just
work.

--
"The party of Lincoln has become the party of John Wilkes Booth."

- Carlos Alazraqui -

Kenny McCormack

unread,
Aug 27, 2020, 11:16:18 AM8/27/20
to
In article <4ed2e301-83e9-4a41...@googlegroups.com>,
hongy...@gmail.com <hongy...@gmail.com> wrote:
...
>> Note: I did this thing first in AWK and it is a little easier to
>> handle in AWK,
>
>What's the awk code used by you for this job? But this will make you
>mix the shell code with awk in your script.

What I meant was that the basic algorithm was originally developed in an
AWK script. When I saw your post, I ported it to shell for your benefit.

--
The randomly chosen signature file that would have appeared here is more than 4
lines long. As such, it violates one or more Usenet RFCs. In order to remain
in compliance with said RFCs, the actual sig can be found at the following URL:
http://user.xmission.com/~gazelle/Sigs/ThePublicGood

Kenny McCormack

unread,
Aug 27, 2020, 11:26:11 AM8/27/20
to
In article <2a82c772-71a1-4ae1...@googlegroups.com>,
hongy...@gmail.com <hongy...@gmail.com> wrote:
...
>> #!/bin/bash
>> do_print() {
>> local str=$(printf "%*s" $((3-(${#1}+1)/8)) "")
>> printf "%s%s" "$1" "${str// /$'\t'}"
>
>It seems so complicated and I'm still not so clear on the logic of the
>above two lines. Any hints?

My guess is that, eventually, someone will explain it all for you, but in
the meantime, you should treat it as a learning experience. Figure it out
for yourself; you'll be glad you did. It is almost completely "bash-isms"
(bash extensions not in POSIX), but everything you need to know is in
"man bash".

One hint: "3" is one more than the maximum number of tabs you will ever want
to print.

--
The randomly chosen signature file that would have appeared here is more than 4
lines long. As such, it violates one or more Usenet RFCs. In order to remain
in compliance with said RFCs, the actual sig can be found at the following URL:
http://user.xmission.com/~gazelle/Sigs/RepInsults

Hongyi Zhao

unread,
Aug 27, 2020, 7:34:37 PM8/27/20
to
Why this method mixing the usage of printf and echo. This makes it not a grace solution, at least from my points of view.

Kenny McCormack

unread,
Aug 29, 2020, 10:31:11 AM8/29/20
to
In article <f01846d3-8af6-469a...@googlegroups.com>,

I realized (too late) that the algorithm that I posted earlier isn't quite
right (although it did produce the right output in the test case), because
bash's shell arithmetic feature (the $(( )) construct) only does integer
math. My algorithm depends on having decimal (fractional) math.

Here is a revised version, with the key part re-written in AWK - along with
a full test suite that shows it doing the right thing all the way from 0 to
24. Note that it would really be nice to have a builtin way to do decimal
math in bash - sounds like a good project to write an extension library to
do that. Note that "bc" almost works - "bc" is my usual goto for doing
this (decimal math in shell), but "bc" has its own set of issues. I
usually end up going back to AWK.

BTW, tested/works on a system where "awk" is really GAWK. That's,
obviously, the best world in which to live, but it should work with other
AWKs - though, no promises.

Anyway, here it is - enjoy!

#!/bin/bash
do_print() {
local str=$(awk 'BEGIN { printf "%*s",4-('${#1}'+1)/8,"" }')
printf "%s%s" "$1" "${str// /$'\t'}"
}
do_print "18888(socks5)";echo "18889(socks5-sticky-session)"
do_print "18886(stats,http)";echo "18887(socks5-health-check)"
for i in {0..24};do
do_print $foo;echo $i
foo=${foo}X
done

--
Marshall: 10/22/51
Jessica: 4/4/79

Janis Papanagnou

unread,
Aug 29, 2020, 11:37:18 AM8/29/20
to
On 29.08.2020 16:31, Kenny McCormack wrote:
> In article <f01846d3-8af6-469a...@googlegroups.com>,
>
> I realized (too late) that the algorithm that I posted earlier isn't quite
> right (although it did produce the right output in the test case), because
> bash's shell arithmetic feature (the $(( )) construct) only does integer
> math. My algorithm depends on having decimal (fractional) math.
>
> [...] Note that it would really be nice to have a builtin way to do decimal
> math in bash - sounds like a good project to write an extension library to
> do that.

Uh-oh! ("If all I have is a hammer..." :-)

I know how difficult it is to switch tools that one is used to, specifically
if it is the shell (the one that is prevalent the default shell on Linux),
but sometimes it's easier to just replace #!/bin/bash by #!/bin/ksh and
you have (besides many other features) the math not restricted to integers.

> Note that "bc" almost works - "bc" is my usual goto for doing
> this (decimal math in shell), but "bc" has its own set of issues. I
> usually end up going back to AWK.

Curious; what bc issues do you mean?

Janis

> [...]


Kenny McCormack

unread,
Aug 29, 2020, 12:34:56 PM8/29/20
to
In article <ridsnb$r6m$1...@news-1.m-online.net>,
Janis Papanagnou <janis_pa...@hotmail.com> wrote:
...
>> [...] Note that it would really be nice to have a builtin way to do decimal
>> math in bash - sounds like a good project to write an extension library to
>> do that.

>I know how difficult it is to switch tools that one is used to, specifically
>if it is the shell (the one that is prevalent the default shell on Linux),
>but sometimes it's easier to just replace #!/bin/bash by #!/bin/ksh and
>you have (besides many other features) the math not restricted to integers.

Yeah, well, I think you've answered your own question. At this point, I'm
pretty invested in bash. Among other things, the fact is that it isn't
just a single machine - I'd have to make the change everywhere and also
whenever a new machine comes online, make it there. It's just too much
trouble. In fact:

1) When I tried out ksh (a few decades ago), I was not impressed. No
doubt it has improved since then, but for whatever it is worth, it
wouldn't be my first choice.

2) I'm a big fan of tcsh. If I were going to make a shell change
(i.e., commit to swimming upstream for the rest of my life), I'd go
with tcsh. In fact, when I first started using Linux, I did
install and use tcsh on each new machine, but after a while, that
just got old.

This is probably a large part of why people often say that you shouldn't
program anything non-trivial in shell - because of the lock-in effect.
Because shells seem to be pretty ephemeral (as compared to lower-level
languages).

>> Note that "bc" almost works - "bc" is my usual goto for doing
>> this (decimal math in shell), but "bc" has its own set of issues. I
>> usually end up going back to AWK.
>
>Curious; what bc issues do you mean?

Well, first of all, I'm not looking for help or workarounds or "You're not
doing it right" type responses. This is just in the spirit of discussion.

And, you know, I'm not even sure I can remember all the things I've run
into. I would probably best describe it as "incomplete" and "not well
documented". It's one of those things where you go with it for a while,
and then you hit something. There's always that feeling that you're going
to hit a wall at any moment. And the man page, while full and complete I'm
sure, is not a thing of beauty.

Here are two examples off the top of my head (I'm sure there are more, but
they don't come to mind at the moment):
1) You can't write a leading plus sign. You can't write +8 as a number.
2) The collection of functions is anemic. F.e., I couldn't find an "int()"
function.

Like I said, it's not really that's there's bugs; it is just "incomplete".

Now, totally changing the subject (but not really), one thing that annoys
me about bash's $(( )) functionality is what I call "the leading zero" bug.
That is, that if you write 08 as a number, it blows up. Yes, I know why
this is not technically a bug, but it is annoying. Neither bc nor awk
suffer from this. But, surprisingly, Perl *does*.

--
BigBusiness types (aka, Republicans/Conservatives/Independents/Liberatarians/whatevers)
don't hate big government. They *love* big government as a means for them to get
rich, sucking off the public teat. What they don't like is *democracy* - you know,
like people actually having the right to vote and stuff like that.

Janis Papanagnou

unread,
Aug 29, 2020, 1:31:16 PM8/29/20
to
On 29.08.2020 18:34, Kenny McCormack wrote:
> In article <ridsnb$r6m$1...@news-1.m-online.net>,
> Janis Papanagnou <janis_pa...@hotmail.com> wrote:
> ...
>>> [...] Note that it would really be nice to have a builtin way to do decimal
>>> math in bash - sounds like a good project to write an extension library to
>>> do that.
>
>> I know how difficult it is to switch tools that one is used to, specifically
>> if it is the shell (the one that is prevalent the default shell on Linux),
>> but sometimes it's easier to just replace #!/bin/bash by #!/bin/ksh and
>> you have (besides many other features) the math not restricted to integers.
>
> Yeah, well, I think you've answered your own question. At this point, I'm
> pretty invested in bash. Among other things, the fact is that it isn't
> just a single machine - I'd have to make the change everywhere and also
> whenever a new machine comes online, make it there. It's just too much
> trouble. In fact:
>
> 1) When I tried out ksh (a few decades ago), I was not impressed. No
> doubt it has improved since then, but for whatever it is worth, it
> wouldn't be my first choice.
>
> 2) I'm a big fan of tcsh. If I were going to make a shell change
> (i.e., commit to swimming upstream for the rest of my life), I'd go
> with tcsh. In fact, when I first started using Linux, I did
> install and use tcsh on each new machine, but after a while, that
> just got old.

Well, I understand the tcsh thing for use as interactive shell. But the
other shells have the same POSIX core, and those extensions (with only few
exceptions) that are often referred here as "bashisms" mostly stem from
ksh. So the difference in the core should not be that big. Moreover bash
seems to have tried to catch up with ksh and adopted a lot of features.
But okay, I stop here.

> This is probably a large part of why people often say that you shouldn't
> program anything non-trivial in shell - because of the lock-in effect.
> Because shells seem to be pretty ephemeral (as compared to lower-level
> languages).
>
>>> Note that "bc" almost works - "bc" is my usual goto for doing
>>> this (decimal math in shell), but "bc" has its own set of issues. I
>>> usually end up going back to AWK.
>>
>> Curious; what bc issues do you mean?
>
> Well, first of all, I'm not looking for help or workarounds or "You're not
> doing it right" type responses. This is just in the spirit of discussion.

I was just curious; for two reasons. First, in the past I observed a bug
that appeared when calculating in the 1000-digit numbers range (as far as
memory serves), so it appeared to be not reliable to use. But then I read
somewhere that the standard supports anyway just 100 digits (or so), so I
took it that I was working outside the specification range (which was not
satisfying anyway since GNU tools usually shift limits or define no limit).
The second reason is that I occasionally use "bc" for ad hoc calculations,
therefore I have some interest to know any existing issues.

> And, you know, I'm not even sure I can remember all the things I've run
> into. I would probably best describe it as "incomplete" and "not well
> documented". It's one of those things where you go with it for a while,
> and then you hit something. There's always that feeling that you're going
> to hit a wall at any moment. And the man page, while full and complete I'm
> sure, is not a thing of beauty.
>
> Here are two examples off the top of my head (I'm sure there are more, but
> they don't come to mind at the moment):
> 1) You can't write a leading plus sign. You can't write +8 as a number.
> 2) The collection of functions is anemic. F.e., I couldn't find an "int()"
> function.
>
> Like I said, it's not really that's there's bugs; it is just "incomplete".
>
> Now, totally changing the subject (but not really), one thing that annoys
> me about bash's $(( )) functionality is what I call "the leading zero" bug.
> That is, that if you write 08 as a number, it blows up. Yes, I know why
> this is not technically a bug, but it is annoying. Neither bc nor awk
> suffer from this. But, surprisingly, Perl *does*.

In cases where you may have (in shell) leading zeroes I suggest to use
the base prefix, which would be 10#08 for your example or 10#"${var}"
if the value string stems from undefined input channels.

Janis

0 new messages