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