Bash shell script output alignment
Solution 1
Use printf
to format output (it's also more portable than echo
). I would also store the real value of the colour escape sequences instead of storing them in a form that requires expansion by echo
.
RED=$(tput setaf 1) GREEN=$(tput setaf 2) YELLOW=$(tput setaf 3)
NC=$(tput sgr0)
online="${GREEN}online$NC" offline="${RED}offline$NC"
ping -c 1 "$hostname" > /dev/null 2>&1 && state=$online || state=$offline
printf 'Network %-15s: %s\n' "$hostname" "$state"
%-15s
is a format specification that pads the strings with spaces on the right so as the length (in number of characters in zsh
and fish
and bytes in most other shells/printf) to be at least 15.
$ printf '|%-4s|\n' a ab abc abcd abcde
|a |
|ab |
|abc |
|abcd|
|abcde|
printf '|%4s|\n' a ab abc abcd abcde
| a|
| ab|
| abc|
|abcd|
|abcde|
With truncation:
$ printf '|%.4s|\n' a ab abc abcd abcde
|a|
|ab|
|abc|
|abcd|
|abcd|
$ printf '|%4.4s|\n' a ab abc abcd abcde
| a|
| ab|
| abc|
|abcd|
|abcd|
$ printf '|%-4.4s|\n' a ab abc abcd abcde
|a |
|ab |
|abc |
|abcd|
|abcd|
Other utilities to format text in columns include POSIX expand
:
printf 'Network %s\t: %s\n' "$hostname" "$state" | expand -t 30
(here expanding the TAB character (\t
) with tab stops every 30 columns)
Or BSD column
or POSIX pr
:
printf 'Network %s\n: %s\n' "$hostname" "$state" | pr -at2
(here outputting on 2 36-column wide columns (see the -w
option to change the page width from the default of 72)).
or BSD rs
:
{
while...
printf 'Network %s\n: %s\n' "$hostname" "$state"
done
} | rs -e 0 2
(like column
won't start outputting until it has read all the input).
Or GNU columns
:
printf 'Network %s\n: %s\n' "$hostname" "$state" | columns -w 25 -c 2
zsh
also has some parameter expansion flags for string padding: ${(l:15:)hostname}
for left padding and ${(r:15:)hostname}
for right padding (with truncation). In prompt expansion (like in prompts or in print -P
or as enabled in parameter expansions with the %
flag), it also supports %F{green}
for colour output, so you can do:
online='%F{green}online%f'
printf '%s\n' "Network ${(r:15:)hostname}: ${(%)online}"
Or:
print -rP "Network ${(r:15:)hostname}: $online"
Though the content of $hostname
would then also be subject to prompt expansion,
which would constitute a command injection vulnerability if the content of $hostname
was not under your control (like in hostname='%<a[`reboot`]<'
)
Solution 2
Simply with column
command:
yourscript.sh | column -t
The output:
Network 10.x.xx.xxx : Online
Network 10.x.xx.xxx : Offline
Network 10.x.xx.xxx : Offline
Network 10.x.xx.xxx : Offline
Network 10.x.xx.x : Online
Network 139.xxx.x.x : Online
Network 208.xx.xxx.xxx : Online
Network 193.xxx.xxx.x : Online
Solution 3
Update your script to insert a set number to \t
(tabs) where you want to tab out to a column.
Output something simular to the following would give you the required alignment:
Network 10.x.xx.xxx\t: Online
Network 10.x.xx.xxx\t: Offline
Network 10.x.xx.xxx\t: Offline
Network 10.x.xx.xxx\t: Offline
Network 10.x.xx.x\t: Online
Network 139.xxx.x.x\t: Online
Network 208.xx.xxx.xxx\t: Online
Network 193.xxx.xxx.x\t: Online
Solution 4
To display even better than @Roman
yourscript.sh | column -t -s $'\t'
Then add \t
in each line to split it into column.
Related videos on Youtube
pijaaa
Updated on September 18, 2022Comments
-
pijaaa almost 2 years
My script:
date echo -e "${YELLOW}Network check${NC}\n\n" while read hostname do ping -c 1 "$hostname" > /dev/null 2>&1 && echo -e "Network $hostname : ${GREEN}Online${NC}" || echo -e "${GRAY}Network $hostname${NC} : ${RED}Offline${NC}" done < list.txt sleep 30 clear done
Is outputting info like this:
Network 10.x.xx.xxx : Online Network 10.x.xx.xxx : Offline Network 10.x.xx.xxx : Offline Network 10.x.xx.xxx : Offline Network 10.x.xx.x : Online Network 139.xxx.x.x : Online Network 208.xx.xxx.xxx : Online Network 193.xxx.xxx.x : Online
which I'd like to clean up to get something like this:
Network 10.x.xx.xxx : Online Network 10.x.xx.xxx : Offline Network 10.x.xx.xxx : Offline Network 10.x.xx.x : Online Network 139.xxx.x.x : Online Network 208.xx.xxx.xxx : Online Network 193.xxx.xxx.x : Online Network 193.xxx.xxx.xxx : Offline
-
pijaaa over 6 yearsim sorry for stupid question, but where i should put that command?
-
RomanPerekhrest over 6 years@pijaaa, see my update, pipe with your script output
-
Stephen Kitt over 6 yearsIt would be worth adding a short example based on the script in the question...
-
Stefan Wienströer over 6 years@StephenKitt ty Stephen, I was trying to figure out how to make it more than just one line, and this honestly didn't occur to me!
-
Stéphane Chazelas over 6 yearsYou'll notice that with the default tabstops every 8 columns,
printf 'Network %s\t: Online\n' 8.8.8.8 192.168.122.123
doesn't align properly. You could work around that by usingexpand
to expand the tabs with a different tab stop as shown in my answer. -
Stéphane Chazelas over 6 yearsNote that
column
(a BSD command, also ported to Linux and found by default on some distributions, not to be confused with GNUcolumns
) will need to read the whole input before it can start outputting something as it needs to compute the width of the columns based on the widest one. -
Stefan Wienströer over 6 yearsPlease feel free to accept this as the answer then, by clicking the tick icon.