如何重定向文件上“while”指令的标准输入?

如何重定向文件上“while”指令的标准输入?

我正在学习 shell 脚本,并且在理解编写脚本所需的主要命令结构的开发方面遇到一些困难,尤其是whiledodone命令。

我知道,当从文件重定向指令的标准输入时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 因为您不想通过读取变量的当前值,而是然后姓名变量,以便它知道它应该它的价值。

相关内容