所以我有一个主题文件夹其中包含以下主题:
- 地理
- 数学
这些主题是文件。每一张都包含一名学生的名字和他的分数。
例如,
对于地理
- 马太 15
- 埃琳娜 14
和数学:
- 马太福音10
- 埃琳娜 19
我也有一个学生文件夹目前是空的。该文件夹的目的是放入其中:
- 学生姓名作为文件名;
- 该学生获得的所有科目的分数。
所以这是我的代码:
#### Subjects folder #####
GEOGRAPHY="/home/subject/geography"
MATH="/home/subject/math"
##### Registration student #####
studentMatthew="/home/student/Matthew"
Geo_Matthew=$(grep "Matthew" "$GEOGRAPHY" | cut -d' ' -f2) > "$studentMatthew"
Math_Matthew=$(grep "Matthew" "$MATH" | cut -d' ' -f2) > "$studentMatthew"
studentElena="/home/student/Elena"
Geo_Elena=$(grep "Elena" "$GEOGRAPHY" | cut -d' ' -f2) > "$studentElena"
Math_Elena=$(grep "Elena" "$MATH" | cut -d' ' -f2) > "$studentElena"
##### SHOW RESULT #####
echo "Geography" >> "$studentMatthew" "$Geo_Matthew"
echo "Math" >> "$studentMatthew" "$Geo_Matthew"
echo "Geography" >> "$studentElena" "$Geo_Elena"
echo "Math" >> "$studentElena" "$Math_Elena"
这非常有效。但这样写并不是一个很好的做法。
因为如果我添加另一个学生,比如说帕特里克,我就必须手动添加他的地理和数学分数。
所以它会是:
##### Registration Patrick #####
studentPatrick="/home/student/Patrick"
Geo_Patrick=$(grep "Patrick" "$GEOGRAPHY" | cut -d' ' -f2) > "$studentPatrick"
Math_Patrick=$(grep "Patrick" "$MATH" | cut -d' ' -f2) > "$studentPatrick"
##### SHOW RESULT PATRICK #####
echo "Geography" >> "$studentPatrick" "$Geo_Patrick"
echo "Math" >> "$studentPatrick" "$Math_Patrick"
而且会很多余..
所以我想知道,
有没有办法将此代码重构为循环?那么如果我添加一个新学生,我就不必再次手动编写了?
答案1
for subjfile in /home/subject/*; do
awk -v subj="$(basename "$subjfile")" '{ print subj, $2 >>"/home/student/" $1 }' "$subjfile"
done
这将迭代所有主题文件。该$subjfile
值可能类似于/home/subject/Math
或/home/subject/Geography
等等,具体取决于可用的主题。
程序awk
将依次打开每个文件并打印主题名称(即$subjfile
、 ieMath
或中路径名的“基本名称” Geography
),后跟该主题中的标记($subjfile
文件的第 2 列)。
这将被写入到"/home/student/" $1
which is 下的路径名/home/student
。是$1
第一的文件的列$subjfile
,这是学生的姓名。
该代码假定student
脚本开始运行时目录为空(或者它将附加到其中的文件)。
运行给定的数据(包括其中一个名称的拼写错误),这将在以下位置创建三个文件/home/student
:
$ cat Elena
Geography 14
Math 19
$ cat Mattew
Geography 15
$ cat Matthew
Math 10
请注意,学生姓名是从下面的文件中选取的/home/subject
,并且从未在脚本中硬编码。此外,各种主题取决于/home/subject
目录中可用的文件。
您想在不涉及以下内容的情况下执行此操作吗awk
?
for subjfile in /home/subject/*; do
subject=$( basename "$subjfile" )
while read -r name marks; do
printf '%s %d\n' "$subject" "$marks" >>"/home/student/$name"
done <"$subjfile"
done
答案2
在我的脑海里,你做了这样的事情:
# loop over all files in the student directory
for studentfile in /home/student/*; do
# take just the name of the student (the file name without the path)
studentname="${studentfile##*/}"
# uncomment the next line to clear the student files before populating them
# > "$studentfile"
# loop over all subject files
for subject in /home/subject/*; do
# again, remove the path
subject="${subject##*/}"
grep "$studentname" "$subject" | cut -d' ' -f2 >> "$studentfile"
done
done
假设学生和科目的文件名与文件中用于它们的标签完全相同,并且每个学生已经存在一个文件。
扩展采用删除了最长前缀匹配${var##*/}
的值,即仅留下路径。对于学生来说,我们需要两者,因为我们仅使用but来进行输出重定向。$var
*/
Matthew
grep
/home/student/Matthew
请注意,您的作业Geo_Matthew=$(grep "Matthew" "$GEOGRAPHY" | cut -d' ' -f2) > "$studentMatthew"
中有一个额外的重定向,该作业不会创建任何输出,因此重定向仅截断$studentMatthew
.您只需将 的输出重定向grep
到文件并删除命令替换、变量Geo_Matthew
和后面的echo
.
使用 awk 或 Perl 脚本而不是 shell 脚本会更好,但上面是安排循环中命令的可能方法。