I would like to create a table with two columns. The first column contains the key name and the second column contains the value. The value can be text that usually results in multiple lines in the terminal.
With printf
or column
I can easily get the following output:
<----- Terminal Length ------>
key1 This is the value for
key1 with a very long text.
...
But I would like the value to be displayed in the same column like this:
<----- Terminal Length ------>
key1 This is the value for
key1 with a very long
text.
...
How can I wrap a long line inside the same column?
Solution based on andlrc's comment:
columnize2 () {
indent=$1;
collen=$(($(tput cols)-indent));
keyname="$2";
value=$3;
while [ -n "$value" ] ; do
printf "%-10s %-${indent}s\n" "$keyname" "${value:0:$collen}";
keyname="";
value=${value:$collen};
done
}
longvalue=---------------------------------------------------------------------xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
columnize2 30 key1 $longvalue
key1 --------------------------------------------------
-------------------xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzz
tput cols
returns the amount of characters per line in the terminal window. We use this to determine how many characters of the value we can print per line (collen
). If it does not fit on one line, the rest is printed on the following lines.
%-10s
in the print statement is used to allocate 10 characters to display the keyname (long keys are not handled well).
%-${indent}s
is used to indent the value by #indent characters.
Only print as many characters of the value as will fit on one line:${value:0:$collen}
and strip the already printed characters from the value value=${value:$collen}
For following lines we don't print the keyname (by setting it to an empty string).
Actually, the util-linux 'column' command can do it. It is very versatile.
#!/bin/bash
cat <<- EOF | column --separator '|' \
--table \
--output-width 30 \
--table-noheadings \
--table-columns C1,C2 \
--table-wrap C2
key1|Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
key2|blahhhhhhhhhhhhhhhhhhhhhhhhhhhhzzz
EOF
This gives :
key1 Lorem ipsum dolor sit am
et, consectetur adipisci
ng elit, sed do eiusmod
tempor incididunt ut lab
ore et dolore magna aliq
ua.
key2 blahhhhhhhhhhhhhhhhhhhhh
hhhhhhhzzz
--output-width
: give desired size (can use 'tput cols' as described above)
--table-columns C1,C2
: give names to columns to be used with other options
--table-wrap C2
: wrap column 2
Column version :
# column -V
column from util-linux 2.33.1
I was wondering the same question recently. I wrote this small script called printf-wrap
to achieve this feature, inspired by the top answer by @phobic. The reason why it is necessary is that the version of column
on a user's workstaion may not have wrapping capability and he/she may not be authorized to install or update any packages either. (Yes, the user is me ...)
printf-wrap
accepts exactly the same input arguments as printf
, and wrap smartly the text within every columns. And you can use tput
to adjust it to terminal width.
# printf-wrap
# Description: Printf with smart word wrapping for every columns.
# Usage: print-wrap "<format>" "<text>" "<text>" ...
function printf-wrap {
# help info
help_message="Usage: print-column-wrap \"<format>\" \"<text>\" \"<text>\" ..."
if [[ "$1" =~ ^(-|--)?(h|help)$ ]] ; then echo "$help_message" ; return 0 ; fi
# parse argument
format="$1"
width=( $( echo "$format" | sed -r 's/%%|\\%//g' | sed -r 's/%/\n%/g' | sed -n -r 's/^%[^1-9]*([0-9]+).*/\1/p' ) )
shift
text=( "$@" )
error_message="Error: number of input text fields (${#text[@]}) MUST NOT EXCEED number of columns specified (${#width[@]}) !"
if (( ${#text[@]} > ${#width[@]} )) ; then echo "$error_message" ; return 1 ; fi
# printing
while [ -n "$( echo "${text[@]}" | sed -r 's/\s+//g' )" ] ; do
text_cut=()
width_cut=()
for i in ${!text[@]} ; do
text[$i]="$( echo "${text[$i]}" | sed -r 's/^\s+//' )"
if [[ "${text[$i]:${width[$i]}-1:2}" =~ [a-zA-Z0-9_]{2} ]] ; then
text_cut[$i]="$( echo "${text[$i]:0:${width[$i]}}" | sed -r 's/\w+$//' )"
else
text_cut[$i]="${text[$i]:0:${width[$i]}}"
fi
width_cut[$i]=${#text_cut[$i]}
text[$i]="${text[$i]:${width_cut[$i]}}"
done
printf "$format" "${text_cut[@]}"
done
}
The ouput looks like the following.
$ printf-wrap "%-10s %-$(( $(tput cols) - 11 ))s\n" "key1" "This is the value for key1 with a very long text"
============ Terminal Width ============
key1 This is the value for key1
with a very long text
Hope this can help anyone. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With