Bash 用壳反三角形

Bash 用壳反三角形,bash,shell,unix,reverse,Bash,Shell,Unix,Reverse,好的,我已经做了几天了,我对bash UNIX系统还不太熟悉,我刚刚开始学习,但我正在尝试编写一个脚本,其中用户输入一个整数,脚本将取该整数,并使用输入的整数作为基数打印出一个三角形,然后递减,直到它达到零。例如: reverse_triangle.bash 4 **** *** ** * 这就是我到目前为止所做的,但当我运行它时,什么都没有发生,我不知道是什么问题 #!/bin/bash input=$1 count=1 for (( i=$input; i>=$count;i--

好的,我已经做了几天了,我对bash UNIX系统还不太熟悉,我刚刚开始学习,但我正在尝试编写一个脚本,其中用户输入一个整数,脚本将取该整数,并使用输入的整数作为基数打印出一个三角形,然后递减,直到它达到零。例如:

reverse_triangle.bash 4
****
***
** 
*
这就是我到目前为止所做的,但当我运行它时,什么都没有发生,我不知道是什么问题

#!/bin/bash
input=$1
count=1
for (( i=$input; i>=$count;i-- ))
       do 
           for (( j=1; j>=i; j++ ))
              do 
                   echo -n "*"
            done
       echo 
       done
exit 0

当我试着运行它时,什么也没发生,它只是转到下一行。非常感谢您的帮助:)

正如我在评论中所说,您的测试是错误的:您需要

for (( j=1; j<=i; j++ ))
否则,此循环仅在
i=1
时执行,并成为无限循环


现在如果你想用另一种更好的方法来解决这个问题:

#!/bin/bash

[[ $1 = +([[:digit:]]) ]] || { printf >&2 'Argument must be a number\n'; exit 1; }
number=$((10#$1))
for ((;number>=1;--number)); do
    printf -v spn '%*s' "$number"
    printf '%s\n' "${spn// /*}"
done
为什么更好?首先,我们检查参数是否真的是一个数字。否则,您的代码将受到任意代码注入的影响。此外,我们还要确保数字是以10为基数,用
10#$1
理解的。否则,像
09
这样的参数将引发错误

我们实际上不需要为循环添加额外的变量,提供的参数已经足够好了。现在的诀窍是:要打印n次模式,一个很酷的方法是使用
printf
在变量中存储n个空格:
%*s
将扩展到n个空格,其中n是
printf
找到的相应参数
例如:

printf '%s%*s%s\n' hello 42 world
将打印:

hello                                     world
(有42个空格)

编者按:
%*s
通常不会扩展到n个空格,正如上面的输出所证明的那样,该输出包含37个空格。
相反,映射到
*
的参数
42
s
字段的字段宽度,它映射到以下参数
world
,导致字符串
world
的长度被填充为42;由于
world
的字符计数为5,因此37个空格用于填充。
要使示例按预期工作,请使用
printf“%s%*s%s\n“hello 42”world
-注意
42
后面的空字符串参数,它确保整个字段由填充组成,即空格(如果后面没有参数,则会得到相同的效果
42

使用
printf
-v
选项,我们可以将
printf
格式的任何字符串存储到变量中;这里我们在
spn
中存储
$number
空格。最后,我们使用扩展名
${spn//*}
将所有空格替换为字符
*


还有一种可能性:

#!/bin/bash

[[ $1 = +([[:digit:]]) ]] || { printf >&2 'Argument must be a number\n'; exit 1; }
printf -v s '%*s' $((10#1))
s=${s// /*}
while [[ $s ]]; do
    printf '%s\n' "$s"
    s=${s%?}
done
这次我们使用前面的技术构造变量
s
,该变量包含一组
*
(由用户给定的数字)。然后我们有一个
while
循环,它在
s
为非空时循环。在每次迭代中,我们打印
s
的内容,并使用扩展名
${s%?}
删除
s

的最后一个字符,该扩展名基于:

以下内容更简单,性能也更好:

#/bin/bash
计数=$1#(…为简洁起见省略了数字验证代码)
#创建第1行,由$count'*'字符组成,并存储在变量$line中。
printf-v行“%.s*”$(seq$count)
#从$倒计时到1。
而((计数-);做
#根据当前值$count打印第1行的*子字符串*。
printf“%.${count}s\n”“$line”
完成
  • printf-v行'*%.s'$(seq$count)
    是一种打印
    *
    $count
    次的技巧,这得益于
    %.s*
    为提供的每个参数生成
    *
    ,而不管参数的值如何(得益于
    %.s
    ,它实际上忽略了它的参数)
    $(seq$count)
    展开为
    $count
    参数,生成由
    $count
    *
    字符组成的字符串。总体而言,由于
    -v line
    ,它存储在变量
    $line
  • printf“%.${count}s\n”“$line”
    $line
    的开头打印一个子字符串,即
    $count
    字符。长

对不起,应该说是反向三角形。bash您的测试是错误的:应该是
j=i
。这里有一些很棒的技术。两个观察结果:(a)通过使用
[$1=+([[:digit:]])]
,即扩展的glob,您依赖于bash4.1+;为了使它也能在3.2+上工作,您可以使用正则表达式匹配:
[[$1=~^[:digit:]+$]
。(续下)
#!/bin/bash

[[ $1 = +([[:digit:]]) ]] || { printf >&2 'Argument must be a number\n'; exit 1; }
printf -v s '%*s' $((10#1))
s=${s// /*}
while [[ $s ]]; do
    printf '%s\n' "$s"
    s=${s%?}
done