我正在学习 shell 脚本,并且在理解编写脚本所需的主要命令结构的开发方面遇到一些困难,尤其是while
、do
和done
命令。
我知道,当从文件重定向指令的标准输入时while
,一旦读取整个文件,它将停止(在这种情况下,文件是逐行读取的,当前行放置在使用的变量中read
):
while read lig fich
do
...
done < fich
这是否意味着我们有整个当前行fich
?
例如:此脚本采用用户名和字符串作为参数,它搜索用户名拥有且名称包含字符串的文件:
#usage nblign nom-utilisateur chaine
find . -user $1 | grep $2 >temp
while read lig temp
do
echo $lig "nombre de ligne" `wc -l < $lig`$
done < temp
rm temp
在这里,我的老师$
之前遗漏了lig
,这while
是一个错字吗?因为为了获得命令或变量的结果,必须使用$
它来检索它。
答案1
你有几个误解。首先,while read ... do
您尝试使用的格式是:
while read var; do ...; done < file
并不是
while read var file; do ...; done < file
基本上,while read var; do ...; done < file
将读取每一行file
并将其保存为var
.read
和之间的任何值do
都被视为变量。如果您给出多个变量,则该行将在空白处进行分割(默认情况下,在变量的值$IFS
,\t
和\n
空格上)并保存到给定的变量中。正如中所解释的help read
:
从标准输入读取一行,如果提供了 -u 选项,则从文件描述符 FD 读取一行。与单词拆分一样,该行被拆分为多个字段,第一个单词分配给第一个 NAME,第二个单词分配给第二个 NAME,依此类推,所有剩余单词分配给最后一个 NAME。
因此,例如:
$ echo "foo bar baz zab" | while read v1 rest; do echo "v1:$v1, rest:$rest"; done
v1:foo, rest:bar baz zab
$ echo "foo bar baz zab" | while read v1 v2 rest; do echo "v1:$v1, v2:$v2, rest:$rest"; done
v1:foo, v2:bar, rest:baz zab
$ echo "foo bar baz zab" | while read v1 v2 v3 rest; do echo "v1:$v1, v2:$v2, v3:$v3, rest:$rest"; done
v1:foo, v2:bar, v3:baz, rest:zab
正如您在上面所看到的,输入行被分为您给出的尽可能多的变量。当输入中的变量名称少于“单词”时,最后一个变量将获取该行的其余部分。这与从文件读取时完全相同。
然后,使用var="foo"
和设置变量读使用$var
。所以不,你的老师是对的,你不希望$
定义变量的时间。因此while read var
是正确的也是while read $var
错误的。
因此,使用相同逻辑的脚本的工作版本将是:
find . -user $1 | grep $2 >temp
while read lig
do
echo $lig "nombre de ligne" `wc -l < $lig`
done < temp
rm temp
请注意,我temp
从行尾删除了read
和。我不知道你为什么把它放在那里。$
echo
带有变量的更好版本的脚本正确引用,使用find
查找相关文件而不是尝试解析并且没有不必要的临时文件将是:
find . -user "$1" -name "*$2*" |
## No need for a temp file, just pipe the output directly
## to the while loop
while read lig
do
echo "$lig nombre de ligne: $(wc -l < "$lig")"
done
最后,一个真正强大的方法,与上面的不同,可以处理任意文件名,包括那些带有空格或其他奇怪字符的文件名:
find . -user "$1" -name "*$2*" -print0 |
## No need for a temp file, just pipe the output directly
## to the while loop
while IFS= read -r -d '' lig
do
echo "$lig nombre de ligne: $(wc -l < "$lig")"
done
答案2
来自help read
:“从标准输入读取一行并将其拆分为字段。”。换句话说,它读取整行,并尝试将其分成 3 个不同的部分,并将每个部分存储到您命名的 3 个变量中。请注意,如果线上只有一个部分,则不会设置其他两个变量。您不使用$
There 因为您不想通过读取变量的当前值,而是然后姓名变量,以便它知道它应该放它的价值。