【问题标题】:How can I multiply a character or string in native POSIX shell script?如何在本机 POSIX shell 脚本中乘以一个字符或字符串?
【发布时间】:2021-02-07 01:53:27
【问题描述】:

这不是另一个问题的重复,原因有两个。该问题没有指定答案必须是 POSIX。该问题的标记答案无法在 Dash shell 中正确运行。 (我不知道如何判断答案是否是 POSIX,但它没有在最新版本的 Dash 上运行。

为了澄清,我只想使用 POSIX shell 内置函数。我应该更清楚地说明这一点。据我所知,seq 是一个 GNU 实用程序,这意味着我必须生成一个外部程序。我不想产生任何外部程序。

我想知道在 shell 脚本中乘以字符串的最有效方法。例如脚本将执行如下...

./multiplychar "*" "5"
*****
./multiplychar "*" "1"
*
./multiplychar "*" "0"

# It would not output anything

我似乎再也找不到它了,但我很久以前就为 bash 找到了这个问题。然而,所有答案都使用了像 Perl e.x 这样的外部程序。 perl -E "print '$' x $2" 或使用 bashisms。

我想知道如何在 POSIX shell 中本地完成这项工作。我正在使用破折号。如果有多种方式,如果出于效率考虑,我将处理小于 100 的小数字。(这是一个严格基于文本的音量条通知的脚本。)

外壳:破折号

操作系统:Arch Linux

【问题讨论】:

标签: shell posix dash-shell


【解决方案1】:

这个简短的 bash 脚本可能很有用。它提供了您想要的输出(在 macOS 上使用GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18) 进行了测试)。我没有用 dash 测试过它(没有真正使用它),但这似乎没有使用任何 bashisms。

#!/usr/bin/env bash

str=''

for i in `seq 1 $2` ; do
    str="${str}${1}"
done

echo "${str}"

如果 seq 不符合 POSIX 或 dash 标准,也许这可以解决问题(同样,仅使用 bash 进行测试):

#!/bin/sh

i=1
str=''

while [ "$i" -le "$2" ]
do
    i=`expr $i + 1`
    str="${str}${1}"
done

echo "${str}"

例子:

$ ./test1.sh '*' 5
*****
$ ./test1.sh '*' 1
*
$ ./test1.sh '*' 0

* 当然必须引用,但数字不需要。

【讨论】:

  • POSIX shell 具有算术功能,因此expr $i + 1 可以替换为$((i + 1))
【解决方案2】:

一个简单的无循环 POSIX 函数,它构建一个足够长(125 个字符)的字符串,并使用 shell 的内置 printf 来打印其中的一些:

multiplychar() { n="$1" 
                 n="$n$n$n$n$n"
                 n="$n$n$n$n$n"
                 n="$n$n$n$n$n"
                 printf %."$2"s\\n "$n" ; }

试运行:

$ multiplychar x 5
xxxxx
$ multiplychar x 0

$ multiplychar b 9
bbbbbbbbb

注意上面的代码其实是一个乘法字符串函数:

$ multiplychar yow! 12
yow!yow!yow!

...它打印 12 个字符的重复字符串。如果字符串的第一个字符就是我们想要的,更改第一行代码将截断字符串:

multiplychar() { n="${1%%${1#?}}"
                 n="$n$n$n$n$n"
                 n="$n$n$n$n$n"
                 n="$n$n$n$n$n"
                 printf %."$2"s\\n "$n" ; }

测试:

$ multiplychar yow! 12
yyyyyyyyyyyy

【讨论】:

  • 据我所知,这是迄今为止唯一没有产生外部程序的 aswer。我现在不会接受它,因为它似乎效率极低,并且它可以输出多少个字符。我知道我说过我正在处理小数字,但如果我可以使用大数字,我仍然会喜欢它。它仍然应该是一个可概括的脚本。
  • @KNOBPersonal,不用多用会更有效率。也就是说,如果需要五行“####...”,请不要运行该函数五次。运行该函数一次,将值分配给一个变量:x= "$(multiplychar '#' 80)",然后在需要的地方使用 echo "$x" 五次。
  • 我的脚本正在根据当前音量调整必要的字符数。因此我不能使用单个变量。它需要随音量更新,以便经常运行该功能。恐怕它对我的需求来说太慢了;它会成为脚本的瓶颈。
【解决方案3】:

我找到的最短的几乎是这样的,没有循环,没有seq

已编辑基于 MemReflect 的评论)

$ printf "%9s" ' ' | tr ' ' x 
xxxxxxxxx

所以你可以这样做:

#!/bin/sh
if [ $2 -eq 0 ]; then
    exit
fi
printf "%${2}s\n" ' ' | tr ' ' "$1"
$ ./multiplychar "*" "5"
*****
$ ./multiplychar "*" "1"
*
$ ./multiplychar "*" "0"
$
【解决方案4】:

完全 POSIX 的做法是使用以下脚本。该脚本不创建任何外部进程,仅使用内置功能。

#!/bin/sh

_seq() (
    i=0 buf=
    while [ "$(( i += 1 ))" -le "$1" ]; do
        buf="$buf $i "
    done
    printf '%s' "$buf"
)

buf=
for i in $(_seq "$2"); do
    buf="$buf$1"
done
printf '%s\n' "$buf"

调用此脚本./multiplychar "*" 5 将打印*****

这是这个脚本的工作原理,

_seq() 函数在子shell 中运行,因此它不会影响任何变量。调用_seq 5 将输出" 1 2 3 4 5 "for 循环中的空格无关紧要。

_seq() 函数和主函数中,输出都存储在一个名为$buf 的变量中,因此我们只调用一次printf

这应该是在您的 shell 上拥有此功能的最有效和 POSIX 方式。

【讨论】:

  • 这正是我想要的!
猜你喜欢
  • 2018-12-05
  • 2016-12-16
  • 1970-01-01
  • 2012-12-21
  • 2013-01-08
  • 2012-10-24
  • 1970-01-01
  • 2012-05-08
相关资源
最近更新 更多