在从书本上学习 bash/ksh 时。我遇到了一个练习,它的结果我很想理解。
当使用 bash -x 执行此脚本以尝试了解其行为(调试)时,我注意到行变量是数字+文件名。因为这是 wc -c [file] 的结果。但一旦 for 循环设置了 res 变量,它就会以某种方式删除文件名,只显示 res=23
(wc -c 的数字结果),我可以看到它发生,但不太明白为什么?我想我理解 for 循环构造.. for i in xxx 做什么.. 不是 100% 理解这个构造中的中断,我确实知道它是为了打破嵌套循环。
这是练习脚本:
if [ ! $# -eq 2 ] ;then
echo
echo "usage: $0 <location> <FileName>"
echo
exit 1
fi
TMPFILE=/tmp/count
line=$(find "$1" -name "$2" -type f -print | tee $TMPFILE | wc -l)
aant=$line
nr=0
som=0
while [ $nr -lt "$aant" ] ; do
nr=$(( nr +1 ))
bestand=$(head -$nr $TMPFILE | tail -1)
echo -n "$bestand"
line=$(wc -c "$bestand")
for woord in $line ; do
res=$woord
break
done
echo " $res"
som=$(( som + res ))
done
if [ "$aant" -eq 0 ] ; then
echo "No files found"
else
echo
echo "In totaal $aant files take $som bytes of space"
fi
rm $TMPFILE
exit 0
答案1
中for word in $line
,变量的内容line
为分割成单词然后扩展文件名全局变量(另请参阅这些 问题)。然后,循环针对每个结果值(或“单词”)运行一次。但在这种情况下,循环最多只有一次迭代,因为该break
语句会停止循环运行。因此,res
循环的第一次迭代中 set 的值仍然保留,其效果与从 中选取第一个空格分隔的单词相同line
。
会有更好的编写方法,例如我们可以使用 shell 扩展删除文件名。这将输出23
:
line='23 somefilename'
res="${line%% *}"
echo "$res"
或者,只是没有wc
输出文件名,通过将文件重定向到wc
的 stdin,而不是将文件名传递到wc
.比较这两个:
$ wc -c foo.txt
8 foo.txt
$ wc -c < foo.txt
8
答案2
如果我正确理解你的问题,魔法就发生在这里:
for woord in $line ; do
res=$woord
break
done
为了帮助您了解其工作原理,也许考虑一下会有所帮助:
for N in 1 2 3 4 5; do
echo $N
done
echo "N is $N"
接下来,添加“ break
”语句并考虑:
for N in 1 2 3 4 5; do
echo $N
break
done
echo "N is $N"
您能描述一下将break
语句添加到for
循环中的作用吗?
在我看来,我最初引用的四行代码可以减少到只有三行:
for res in $line ; do
break
done
这本质上是一种“笨拙”的说法:
res="$(awk '{print $1}' <<< "$line")"
答案3
检查 的手册页wc -c
。
它输出bytecount filename
为$line
空格分隔的列表。
该for $woord in $line
循环从 中读取第一个变量(直到第一个空格)$list
,即 并将bytecount
其存储在 中$res
。然后,它不会继续读取下一个项目,而是$list
点击break
指令并退出循环。
正如 @ikkachu 所说,有bytecount
比循环更简单的方法来获取 。