I want printf to recognize multi-byte characters when calculating the field width so that columns line up properly... I can't find an answer to this problem and was wondering if anyone here had any suggestions, or maybe a function/script that takes care of this problem.
Here's a quick and dirty example:
printf "## %5s %5s %5s ##\n## %5s %5s %5s ##\n" '' '*' '' '' "•" ''
>## * ##
>## • ##
Obviously, I want the result:
>## * ##
>## • ##
Any way to achieve this?
The best I can think of is:
function formatwidth
{
local STR=$1; shift
local WIDTH=$1; shift
local BYTEWIDTH=$( echo -n "$STR" | wc -c )
local CHARWIDTH=$( echo -n "$STR" | wc -m )
echo $(( $WIDTH + $BYTEWIDTH - $CHARWIDTH ))
}
printf "## %5s %*s %5s ##\n## %5s %*s %5s ##\n" \
'' $( formatwidth "*" 5 ) '*' '' \
'' $( formatwidth "•" 5 ) "•" ''
You use the *
width specifier to take the width as an argument, and calculate the width you need by adding the number of additional bytes in multibyte characters.
Note that in GNU wc, -c
returns bytes, and -m
returns (possibly multibyte) characters.
I will probably use GNU awk:
awk 'BEGIN{ printf "## %5s %5s %5s ##\n## %5s %5s %5s ##\n", "", "*", "", "", "•", "" }'
## * ##
## • ##
You can even write shell wrapper function called printf on top of awk to keep same interface:
tr2awk() {
FMT="$1"
echo -n "gawk 'BEGIN{ printf \"$FMT\""
shift
for ARG in "$@"
do echo -n ", \"$ARG\""
done
echo " }'"
}
and then override printf with simple function:
printf() { eval `tr2awk "$@"`; }
Test it:
# buggy printf binary test:
/usr/bin/printf "## %5s %5s %5s ##\n## %5s %5s %5s ##\n" '' '*' '' '' "•" ''
## * ##
## • ##
# buggy printf shell builin test:
builtin printf "## %5s %5s %5s ##\n## %5s %5s %5s ##\n" '' '*' '' '' "•" ''
## * ##
## • ##
# fixed printf function test:
printf "## %5s %5s %5s ##\n## %5s %5s %5s ##\n" '' '*' '' '' "•" ''
## * ##
## • ##
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