使用 shell 的反三角
Reverse Triangle using shell
好的,所以我已经做了几天了,我对整个 bash UNIX 系统都是新的,我刚接触它,但我正在尝试编写一个脚本,用户输入一个整数和脚本将采用该整数并使用输入的整数作为底数打印出一个三角形并递减直到它达到零。一个例子是:
reverse_triangle.bash 4
****
***
**
*
所以这就是我目前所拥有的,但是当我 运行 没有任何反应时,我不知道出了什么问题
#!/bin/bash
input=
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++ ))
而不是
for (( j=1; j>=i; j++ ))
否则这个循环只有在i=1
的时候才执行,变成死循环
现在,如果您想要另一种更好的方法来解决这个问题:
#!/bin/bash
[[ = +([[:digit:]]) ]] || { printf >&2 'Argument must be a number\n'; exit 1; }
number=$((10#))
for ((;number>=1;--number)); do
printf -v spn '%*s' "$number"
printf '%s\n' "${spn// /*}"
done
为什么更好?首先,我们检查参数是否真的是一个数字。否则,您的代码将受到任意代码注入的影响。此外,我们确保数字在基数 10 中被理解为 10#
。否则,像 09
这样的参数会引发错误。
我们真的不需要循环的额外变量,提供的参数就足够了。现在的诀窍是:要打印 n 次模式,一个很酷的方法是将 n spaces 存储在一个变量中 printf
:%*s
会扩展为nspaces,其中n是[=找到的对应参数19=].
例如:
printf '%s%*s%s\n' hello 42 world
将打印:
hello world
(有 42 spaces)。
编者注:%*s
通常不会扩展为 n spaces,如以上输出所示,其中包含 37spaces.
相反,*
映射到 42
的参数是 s
字段的 字段宽度 ,它映射到 跟随参数,world
,导致字符串world
被左-space-填充到42的长度;由于 world
的字符数为 5,因此 37 space 用于填充。
要使示例按预期工作,请使用 printf '%s%*s%s\n' hello 42 '' world
- 请注意 42
之后的 空字符串 参数,它 确保整个字段都是填充,即 spaces(如果 no 参数后跟 42
,你会得到相同的效果)。
使用printf
的-v
选项,我们可以将任何由printf
格式化的字符串存储到一个变量中;这里我们将 $number
space 存储在 spn
中。最后,我们用字符 *
替换所有 space,使用扩展 ${spn// /*}
.
还有一种可能性:
#!/bin/bash
[[ = +([[: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
count= # (... number-validation code omitted for brevity)
# Create the 1st line, composed of $count '*' chars, and store in var. $line.
printf -v line '%.s*' $(seq $count)
# Count from $count down to 1.
while (( count-- )); do
# Print a *substring* of the 1st line based on the current value of $count.
printf "%.${count}s\n" "$line"
done
printf -v line '*%.s' $(seq $count)
是一个打印 *
$count
次的技巧,这要归功于 %.s*
导致每个提供的参数都为 *
,无论参数如何' 值(感谢 %.s
,它有效地忽略了它的参数)。 $(seq $count)
扩展为 $count
个参数,结果是一个由 $count
*
个字符组成的字符串。总体而言,由于 -v line
,它存储在变量 $line
. 中
printf "%.${count}s\n" "$line"
从 $line
的开头打印一个子字符串,即 $count
个字符。长.
好的,所以我已经做了几天了,我对整个 bash UNIX 系统都是新的,我刚接触它,但我正在尝试编写一个脚本,用户输入一个整数和脚本将采用该整数并使用输入的整数作为底数打印出一个三角形并递减直到它达到零。一个例子是:
reverse_triangle.bash 4
****
***
**
*
所以这就是我目前所拥有的,但是当我 运行 没有任何反应时,我不知道出了什么问题
#!/bin/bash
input=
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++ ))
而不是
for (( j=1; j>=i; j++ ))
否则这个循环只有在i=1
的时候才执行,变成死循环
现在,如果您想要另一种更好的方法来解决这个问题:
#!/bin/bash
[[ = +([[:digit:]]) ]] || { printf >&2 'Argument must be a number\n'; exit 1; }
number=$((10#))
for ((;number>=1;--number)); do
printf -v spn '%*s' "$number"
printf '%s\n' "${spn// /*}"
done
为什么更好?首先,我们检查参数是否真的是一个数字。否则,您的代码将受到任意代码注入的影响。此外,我们确保数字在基数 10 中被理解为 10#
。否则,像 09
这样的参数会引发错误。
我们真的不需要循环的额外变量,提供的参数就足够了。现在的诀窍是:要打印 n 次模式,一个很酷的方法是将 n spaces 存储在一个变量中 printf
:%*s
会扩展为nspaces,其中n是[=找到的对应参数19=].
例如:
printf '%s%*s%s\n' hello 42 world
将打印:
hello world
(有 42 spaces)。
编者注:%*s
通常不会扩展为 n spaces,如以上输出所示,其中包含 37spaces.
相反,*
映射到 42
的参数是 s
字段的 字段宽度 ,它映射到 跟随参数,world
,导致字符串world
被左-space-填充到42的长度;由于 world
的字符数为 5,因此 37 space 用于填充。
要使示例按预期工作,请使用 printf '%s%*s%s\n' hello 42 '' world
- 请注意 42
之后的 空字符串 参数,它 确保整个字段都是填充,即 spaces(如果 no 参数后跟 42
,你会得到相同的效果)。
使用printf
的-v
选项,我们可以将任何由printf
格式化的字符串存储到一个变量中;这里我们将 $number
space 存储在 spn
中。最后,我们用字符 *
替换所有 space,使用扩展 ${spn// /*}
.
还有一种可能性:
#!/bin/bash
[[ = +([[: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
count= # (... number-validation code omitted for brevity)
# Create the 1st line, composed of $count '*' chars, and store in var. $line.
printf -v line '%.s*' $(seq $count)
# Count from $count down to 1.
while (( count-- )); do
# Print a *substring* of the 1st line based on the current value of $count.
printf "%.${count}s\n" "$line"
done
printf -v line '*%.s' $(seq $count)
是一个打印*
$count
次的技巧,这要归功于%.s*
导致每个提供的参数都为*
,无论参数如何' 值(感谢%.s
,它有效地忽略了它的参数)。$(seq $count)
扩展为$count
个参数,结果是一个由$count
*
个字符组成的字符串。总体而言,由于-v line
,它存储在变量$line
. 中
printf "%.${count}s\n" "$line"
从$line
的开头打印一个子字符串,即$count
个字符。长.