使用 printf 或 echo 在字符串前面添加前导字符

使用 printf 或 echo 在字符串前面添加前导字符

如何添加前导字符以将字符串填充到一定长度?

假设我想在字符串前面添加零,如果该字符串短于 20 个字符:

printf '%020s\n' "$line"

但是,这会用前导空格填充字符串,而不是其他任何内容,例如零。

该字符串可以是包含数字和其他字符的随机字符串。

我不想将字符串拆分为 number + rest for printf '%020i' $numberor printf '%020d $number'

我当前的输出是:

         shortstring

我想要的输出是:

000000000shortstring

答案1

只需手动进行填充即可。如果您坚持使用 POSIX 工具,我认为没有更直接的解决方案。

while [ ${#line} -lt 20 ]; do
  line=0$line
done

或者

n=${#line}
while [ $n -lt 20 ]; do
  printf '0'
  n=$((n-1))
done
printf "$line\n"

当然,在 zsh 中,有相应的语法。与上面的代码片段不同,如果字符串长度超过 20 个字符,此代码片段将截断该字符串。如果您不想截断,可以附加字符串的尾部。

print -r ${(l:20::0:)line}
print -r ${(l:20::0:)line}${line:20}

答案2

正如 @Gnouc 已经指出的那样,你已经成功了一半:

line='some chars'
printf '%0'$((20-${#line}))'d%s\n' 0 "$line"

###OUTPUT###
0000000000some chars

当然,即使行长超过 20 个字符,你仍然会得到 0,而这只是如果 printf不会因错误而失败,因为它无法正确处理-格式字符串中的破折号(这显然是更有可能的情况)。不过,这很容易处理:

printf "%.$((((l=20-${#line})>0)*l))d%s\n" 0 "$line"

该格式字符串使用printf's%.精确%.0d标志将其小数参数零填充到指示的长度,或者 - 在出现空或空精度的情况下%.d- 以其他方式将任何前导 0 截断为指示的数字。像这样:

printf '#%.0d%c%.5d' 0000000 ':separator' 4 
#:00004

(注意:该%c格式打印其关联参数的第一个字符)

算术$((表达式))首先将 var 设置$l为 20 减号,然后如果小于或大于 0,则分别${#line}计算为 0 或 1 ,最后将其乘以。因此,当 20-小于零时,您会执行此操作,但当它大于零时,您会执行此操作。$l$l${#line}$((0*l))$((1*l))

这样你总是仅打印与 20 和 之间的差值相同数量的前导零${#line}。像这样:

line='some chars'
printf "#%.$((((l=20-${#line})>0)*l))d%s\n" 0 "$line"
#0000000000some chars

和...

line='some chars'
printf "#%.$((((l=4-${#line})>0)*l))d%s\n" 0 "$line"
#some chars

你可以使用相同的%.精确类型字段的形式s也是如此,例如:

line='some chars'
printf "#%.$((((l=4-${#line})>0)*l))d%.4s\n" 0 "$line"
#some

...根据需要截断。

这里是一个循环 - 以下迭代 5 次:

line=
until line=fivec$line; [ $((l)) -lt 0 ] 
do  printf "#%.$((((l=20-${#line})>0)*l))d%s\n" 0 "$line"
done    
#000000000000000fivec
#0000000000fivecfivec
#00000fivecfivecfivec
#fivecfivecfivecfivec
#fivecfivecfivecfivecfivec

值得注意的是,显示的零根本不包含在任何 shell 变量的值中(更不用说),并且仅按照其格式字符串指示$line自动生成。printf

这是具有更高精度的相同循环%.20s

line=
until line=fivec$line; [ $((l)) -lt 0 ] 
do  printf "#%.$((((l=20-${#line})>0)*l))d%.20s\n" 0 "$line"
done    
#000000000000000fivec
#0000000000fivecfivec
#00000fivecfivecfivec
#fivecfivecfivecfivec
#fivecfivecfivecfivec

您可以用它做的另一件事非常有趣。当您printf将 动态生成 0 的功能与您结合起来时,$IFS您可以根据需要多次使用任何字符串,例如:

IFS=0 ; printf '10 lines\n%s' $(printf %010d) ; unset IFS 

###OUTPUT###
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines

答案3

%s是字符串格式规范。零填充仅定义为数字转换而存在:

0对于 d、i、o、u、x、X、a、A、e、E、f、F、g 和 G 转换说明符,前导零(跟随任何符号或基数指示)用于填充到字段宽度 ...对于其他转换,行为未定义。

如果您$line是数字,请使用:

printf '%020i\n' $line

以获得您想要的结果(或其他数字格式说明符之一)。如果您的值更复杂但以数字开头,您可以使用:

printf '%020i %s\n' $num $restofline

一次将数字和字符串组合在一起。

答案4

如果你可以使用perl

$ perl -e 'print sprintf "%020s\n","shortstring"'
000000000shortstring

对于更一般的情况:

$ perl -e 'print sprintf "%020s\n",shift' shortstring
000000000shortstring

$ perl -e 'print sprintf "%020s\n", $_ for @ARGV' shortstring qwerty
000000000shortstring
00000000000000qwerty

笔记

某些平台可以使用前导零格式化输出字符串,至少在AIX.使用GNU gcc,编译时会出现警告:

$ cat test.c
#include <stdio.h>

int main() {
    printf("%08s\n", "qwerty");
    return 0;
}

然后:

$ gcc -Wall test.c
test.c: In function ‘main’:
test.c:4:5: warning: '0' flag used with ‘%s’ gnu_printf format [-Wformat]

如果您不想使用perl,您可以尝试:

$ line=shortstring
$ printf "%0$((20-${#line}))d%s"  0  $line
000000000shortstring

当字符串长度大于 20 时,此方法无法处理。您可以使用 @mikeserv answer 来处理该问题或使用 before 进行检查printf

相关内容