1

I want to print the first two arguments of a bash function, with the unicode character \u2263 on each side using a two space separation. The thing is that the final unicode must display at column 70. If the character region where $1 and $2 reaches column 68, the excess characters are truncated.

         1         2         3   V     4         5         6         7
1234567890123456789012345678901234567890123456789012345678901234567890

 U  FIRST-ARG                                            SECOND-ARG  U
 U  FIRST-ARG-LONGGGGGGGGGGGGGGGG                        SECOND-ARG  U

I could specify a column number (example choosing 34, depicted by character V) to be the maximum width possible for FIRST-ARG

Have started with using max lengths of cl and cr.

local cl=34 cr=30
printf ' \u2263  %.${cl}s   %.${cr}s  \u2263\n' "$1" "$2"
0

3 Answers 3

2

Note that in bash, printf's %-63.63s pads with spaces and truncates to 63 bytes not characters, so can't be used for formatting / aligning unless all characters are single byte. 99.99% of characters are encoded on more than one byte in UTF-8. Your U+2263 character is encoded on 3 bytes in UTF-8, 4 bytes in GB18030.

bash's ${var:offset:length}, like ksh93's where it copied it from works at character level, but even then that only works if all characters are single-width.

To align text properly, possible approaches are to:

  1. use padding/truncation operators that take into account the display width of characters.
  2. assuming the output goes to a terminal, use control sequences to tell the terminal to write what you want at the write place and erase what you don't want.

For 1, you could use a shell such as ksh93 or zsh that has builtin operators for that instead of bash.

In zsh:

align() printf ' \u2263  %s  \u2263\n' ${(j[  ]mr[63])@}

The r[length] parameter expansion flag (here short for r[length][ ] as space is the default padding characters) right pads to the given length, and with m, that length is in terms of display width.

Or to left-align (and right-pad) $1 and right-align $2, as you later clarified:

align() printf ' \u2263  %s  \u2263\n' "${(mr[31])1} ${(ml[31])2}"

With ksh93, it's even simpler:

align() printf ' \u2263  %-63.63Ls  \u2263\n' "$1  $2"

Where %Ls as opposed to %s takes a string of characters, not bytes and pads and truncates based on their display width.

Or to left-align $1 and right-align $2:

align() printf ' \u2263  %-31.31Ls %31.31Ls  \u2263\n' "$1" "$2"

Note that those don't work if the input contains control characters.

For 2, you could: disable auto-margins, write the whole text, move to column 68 and write " ≣" and erase the rest:

am_off=$(tput rmam) am_on=$(tput smam)
col68=$(tput cr; tput cuf 68)
del_to_eol=$(tput el)
align() {
  printf ' \u2263  %s\u2263%s\n' "$am_off$1  $2$col68  " "$del_to_eol$am_on"
}
0

Let's get 2 long parameters:

set -- '123456789.123456789.123456789.123456789.' '987654321.987654321.987654321.987654321.'

Let's say we want to set V at 40, so we have 40 chars of field1 and 68-40=28 chars for field2

cl=40
cr=$((68-cl))

Now we want to print the first field left-justified and the second right-justified (although that won't be visible with these long parameters). It's not enough to set the field widths in the printf format string, we also need to take only a substring of the parameters:

printf '\u2263  %-*s%*s  \u2263\n' "$cl" "${1:0:cl}" "$cr" "${2:0:cr}"
≣  123456789.123456789.123456789.123456789.987654321.987654321.98765432  ≣

We can see

  • "123456789.123456789.123456789.123456789.", the first 40 chars of the first parameter, and
  • "987654321.987654321.98765432", the first 28 chars of the second.
7
  • I am getting a slight problem with the second argument as it is being printed on the second line. Commented Jan 27, 2023 at 22:05
  • I have identified the problem. why cannot I do local cl=40 cr=$((68-cl)) in a single line? Commented Jan 27, 2023 at 22:16
  • 1
    Because the arithmetic expression is evaluated before the variable assignments. This is documented in the manual: Simple Command Expansion Commented Jan 28, 2023 at 2:03
  • Should this be the correct behaviour, or should it be considered a bug? Commented Jan 28, 2023 at 2:10
  • Is the comment you are referring to in item 1 ? Commented Jan 28, 2023 at 2:28
-3

Huh? What do you need?

If the question is how to manipulate string length, then you can use string functions described here: https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Parameter-Expansion

If you need to print something in formatted way - you can use printf instead of echo. It uses the very same formatting as printf() in C. https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#index-printf

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.