我想从命令行读取多个字符串并读取厕所中的所有字符串。如何进一步在循环中处理(如打印)它们。抱歉,我是新手,请原谅我的新手问题。
#!/usr/bin/bash
echo "Enter the number of names of students"
read classcount
for ((i=1;i<=classcount;i++)); do
read names[${i}]
done
for ((i=1;i<=classcount;i++)); do
echo $names[${i}]
done
答案1
一般来说,最好避免脚本中的用户交互或从命令行使用的任何实用程序,因为它会阻碍自动化。
例如,您调用rm file1 file2 'file 3'
,并不rm
只是期望它询问您要删除的文件数量,然后分别提示每个文件。
同样,在这里,最好列出学生来自命令行参数:
#! /usr/bin/bash -
classcount=$#
names=("$@")
if (( classcount > 0 )); then
echo "Student${name[1]+s}:"
printf ' - %s\n' "${name[@]}"
fi
并调用为:
that-script 'John Doe' 'Alice Smith' ...
或者,以文本文件中每行一个的学生列表为例(假设是 GNU xargs
):
xargs -d '\n' -a student.list that-script
您还可以从标准输入中每行读取它们,但同样更好的是,无需交互并读取列表直到输入结束,而不是先提示用户进行计数:
#! /usr/bin/bash -
readarray -t names
classcount="${#names[@]}"
if (( classcount > 0 )); then
echo "Student${name[1]+s}:"
printf ' - %s\n' "${name[@]}"
fi
如果 stdin 是终端,用户将用 表示输入结束Ctrl+D。
要从文件中获取列表:
that-script < student.list
read
可用于从 stdin 读取一行,但语法是IFS= read -r lvalue
, 不是read lvalue
。
另请注意,这[...]
是一个通配符,因此应与出现在列表上下文中的所有参数扩展一样被引用。
另外,数组索引(Bourne 数组除外"$@"
)与bash
in ksh
(但与大多数其他 shell 不同)一样,从 0 开始,而不是 1。
因此,对于您的方法,语法应该是:
#! /usr/bin/bash -
PROGNAME=$0
die() {
(( $# == 0 )) || printf >&2 '%s: ERROR: %s\n' "$PROGNAME" "$1"
exit 1
}
IFS= read -rp "Enter the number of names of students: " classcount || die
# validate to avoid command injection vulnerability
shopt -s extglob # only needed in older versions of bash
[[ $classcount = @(0|[123456789]*([0123456789])) ]] ||
die "Not a valid decimal integer number: $classcount"
for (( i = 1; i <= classcount; i++ )); do
IFS= read -rp "Student $i: " 'names[i-1]' ||
die "$(( classcount - i + 1 )) students missing"
done
for (( i=1; i <= classcount; i++)); do
printf 'Student %s: %s\n' "$i" "${names[i-1]}"
done
$name[$i]
在 (t)csh(在 70 年代末首次引入数组的 shell)中有效,尽管您需要$name[$i]:q
确保空格或通配符在其中或 zsh 中没有被特殊处理,但在bash
复制的ksh
' s 阵列设计。
在这方面,ksh 努力向后兼容 Bourne shell,因此echo $name[$i]
会像 Bourne shell 中一样输出与模式匹配的文件列表<contents-of-$name>[<contents-of-$i>]
($name
并且$i
也会进行分割)。
在 ksh/bash 中,您需要${name[i]}
语法(${name[$i]}
或者${name[${i}]}
也可以工作),并且像每个参数扩展一样,确保它被引用 ( "${name[i]}"
) 以防止 split+glob。
任何状况之下,echo
不能用于输出任意数据。
答案2
答案3
即使您想以交互方式请求列表,让脚本通过某些终止符检测列表的末尾可能会更加用户友好,而不是要求用户预先对它们进行计数并输入正确的计数。
(毕竟,计算机应该比普通人更准确地计算行数。仅当您使用具有手动内存管理功能的语言并且不想将数组大小调整为获得更多输入后的大小可能在内存有限的嵌入式环境中有意义,但在“完整”计算机上则没有那么多意义,并且在 shell 中绝对不是。)
相反,您可以要求用户通过输入空行(也可以是其他内容)来终止列表,并将收到的名称附加到数组中,只要您获得非空行即可。此外,如果在处理数据时实际上不需要索引,您可以使用 获取数组条目列表"${names[@]}"
,然后循环遍历:
#!/bin/bash
names=()
echo "Please enter the names of students (end with an empty line)"
while IFS= read -r name && [ "$name" ]; do
names+=("$name")
done
echo "You entered ${#names[@]} name(s): "
for n in "${names[@]}"; do
echo "$n"
done
[ "$name" ]
测试输入的名称是否为空。如果看到文件结尾,循环也将结束。