使用截断符编写 bash 参数

使用截断符编写 bash 参数

我想打印 bash 函数的前两个参数,\u2263每一侧的 unicode 字符使用两个空格分隔。问题是最终的 unicode 必须显示在第 70 列。如果 和 的字符区域$1到达$2第 68 列,则多余的字符将被截断。

         1         2         3   V     4         5         6         7
1234567890123456789012345678901234567890123456789012345678901234567890

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

我可以指定一个列号(例如选择34,由字符 描述V)作为可能的最大宽度FIRST-ARG

已开始使用cl和的最大长度cr

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

答案1

请注意,在bash,printf中用%-63.63s空格填充并截断为 63字节不是字符,因此不能用于格式化/对齐,除非所有字符都是单字节。 99.99% 的字符在 UTF-8 中使用多个字节进行编码。 U+2263 字符在 UTF-8 中编码为 3 个字节,在 GB18030 中编码为 4 个字节。

bash 的${var:offset:length},就像 ksh93 一样,它从字符级别复制它,但即便如此,也只有在所有字符都是单宽度的情况下才有效。

要正确对齐文本,可能的方法是:

  1. 使用考虑字符显示宽度的填充/截断运算符。
  2. 假设输出到达终端,请使用控制序列告诉终端在写入位置写入您想要的内容并删除您不需要的内容。

对于 1,您可以使用具有内置运算符的 shell(例如 ksh93 或 zsh)来代替 bash。

在 zsh 中:

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

参数r[length]扩展标志(这里缩写为r[length][ ]空格是默认的填充字符)right 填充到给定的长度,并且使用m,该长度以显示宽度为单位。

或者左对齐(和右垫)$1和右对齐$2,正如您后来澄清的那样:

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

有了ksh93,就更简单了:

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

%Ls此相反,%s它采用字符串,而不是字节,并根据其显示宽度进行填充和截断。

或者左对齐$1和右对齐$2

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

请注意,如果输入包含控制字符,这些将不起作用。

对于 2,您可以:禁用自动边距,写入整个文本,移至第 68 列,然后写入" ≣"并擦除其余部分:

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"
}

答案2

让我们得到 2 个长参数:

set -- '123456789.123456789.123456789.123456789.' '987654321.987654321.987654321.987654321.'

假设我们要将 V 设置为 40,因此 field1 有 40 个字符,field2 有 68-40=28 个字符

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

现在我们想要打印第一个字段左对齐,第二个字段右对齐(尽管使用这些长参数是不可见的)。在 printf 格式字符串中设置字段宽度是不够的,我们还需要只获取参数的子字符串:

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

我们可以看到

  • “123456789.123456789.123456789.123456789.”,第一个参数的前 40 个字符,以及
  • “987654321.987654321.98765432”,第二个的前 28 个字符。

答案3

啊?你需要什么?

如果问题是如何操作字符串长度,那么您可以使用此处描述的字符串函数: https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Parameter-Expansion

如果您需要以格式化方式打印某些内容 - 您可以printf使用echo.它使用与printf()C 中完全相同的格式。https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#index-printf

相关内容