由于我是shell脚本的新手,exec
命令总是让我感到困惑,在使用while
循环探索此主题时,触发了以下4个问题:
以下语法1和2
之间有什么区别语法1:
while read LINE
do
: # manipulate file here
done < file
语法2:
exec n<&0 < file
while read LINE
do
: # manipulate file here
done
exec 0<&n n<&-
请详细说明exec n<&0 < file
清晰
此exec n<&0 < file
命令是否等同于exec n<file
? (如果没有,那么两者之间有什么区别?)
我在 Bourne shell和旧版本的ksh 中读过一些内容, while
循环的问题是它是在子shell中执行的< / strong>即可。这意味着在while
循环完成后,对脚本环境的任何更改(例如导出变量和更改当前工作目录)可能都不存在。
例如,请考虑以下脚本:
#!/bin/sh
if [ -f “$1” ] ; then
i=0
while read LINE
do
i=`expr $i + 1`
done < “$1”
echo $i
fi
此脚本尝试计算为其指定的文件中的行数作为参数。
在文件上执行此脚本
$ cat dirs.txt
/tmp
/usr/local
/opt/bin
/var
会产生以下错误结果: 0
虽然您使用该命令递增$ i的值
I = expr $i + 1
当while循环完成时,$ i的值不会被保留。
在这种情况下,您需要 更改while
循环中的变量值,然后在循环外使用该值。
解决此问题的一种方法是在进入循环之前重定向STDIN,然后在循环完成后恢复STDIN。
基本语法是
exec n<&0 < file
while read LINE
do
: # manipulate file here
done
exec 0<&n n<&-
我的问题是:
在 Bourne shell和旧版本的ksh 中,虽然已经在SUBSHELL 中执行了WHILE
循环,但此exec
命令如何帮助保留变量值即使在while
循环完成后也是如此,即exec
命令如何完成任务 更改while
循环内的变量值,然后在循环外使用该值
答案 0 :(得分:1)
file
后发生。while
循环在两种情况下从FD 0读取,如果shell支持exec
重定向到<
循环,则while
无意义。要从FD 9中读取,您必须使用done <&9
(POSIX)或read -u 9
(在Bash中)。exec
(在这种情况下,请参阅help exec
/ man exec
)将跟随它的redirections应用于当前shell,并从左到右应用它们。例如,exec 9<&0 < file
将FD 9指向FD 0(标准输入)当前指向的位置,有效地使FD 9成为FD 0的“副本”。之后file
被发送到标准输入(均为FD) 0和9)。在shell中运行shell以查看两者之间的差异(注释解释):
$ echo foo > file
$ exec "$SHELL"
$ exec 9<&0 < file
$ foo # The contents of the file is executed in the shell
bash: foo: command not found
$ exit # Because the end of file, equivalent to pressing Ctrl-d
$ exec "$SHELL"
$ exec 9< file # Nothing happens, simply sends file to FD 9
while
) are not available to the parent shell. 这是设计,而不是错误。这里和USE上的许多其他答案都是指这个。答案 1 :(得分:1)
这么多问题......但是所有问题都是同一个问题的变体,所以我会继续......
没有命令的 exec
用于在当前进程中进行重定向。也就是说,它会更改附加到不同文件描述符(FD)的文件。
我认为应该是这样的。在may系统中,{}
是mandadory:
exec {n}<&0 < file
此行复制FD 0(标准输入)并将新FD存储到n
变量中。然后它将file
附加到标准输入。
while read LINE ; do ... done
此行从标准输入中读取变量LINE
的行,即file
。
exec 0<&n {n}<&-
此行将FD从n
复制回0(原始标准输入),自动关闭file
然后关闭n
(重复的原始标准输入)。
另一种语法:
while read LINE; do ... done < file
做同样的事情,但不那么复杂。
exec {n}<&0 < file
这些是重定向,它们从左到右执行。第一个n<&0
执行dup(0)
(请参阅man dup
)并将结果新FD存储在变量n
中。然后<file
执行open("file"...)
并将其分配给FD 0。
没有。 exec {n}<file
打开文件并将新FD分配给变量n
,保持标准输入(FD 0)不变。
我不知道旧版本的ksh,但通常的问题是在做管道时。
grep whatever | while read LINE; do ... done
然后while
命令在子shell中运行。如果它在管道的左侧,也是如此。
while read LINE ; do ... done | grep whatever
但是对于简单的重定向,没有子shell:
while read LINE ; do ... done < aaa > bbb
关于您的示例脚本,一旦我将印刷报价更改为普通双引号,它就适用于我; - ):
#!/bin/sh
if [ -f "$1" ] ; then
i=0
while read LINE
do
i=`expr $i + 1`
done < "$1"
echo $i
fi
例如,如果文件是test
:
$ ./test test
9
关于您的最新问题,子shell不是由while
创建的,而是由管道|
或可能在旧版本的ksh中通过重定向<
创建的。 exec
技巧的作用是防止重定向,因此不会创建子shell。
答案 2 :(得分:1)
让我无序地回答你的问题。
命令exec n<&0 < file
是无效的语法。可能n
代表&#34;一些任意数字&#34;。那就是说,例如
exec 3<&0 < file
按顺序执行两个重定向:它将标准输入文件描述符(0
)复制/复制为文件描述符3
。接下来,它重定向&#34;文件描述符0
从文件file
中读取。
稍后,命令
exec 0<&3 3<&-
首先从保存的文件描述符3
复制标准输入文件描述符,将标准输入重定向回其先前的源。然后它关闭文件描述符3
,它的目的是备份最初的stdin
。
实际上,这两个示例都是相同的:它们暂时将stdin重定向到while
循环的范围内。
Nope:exec 3<filename
使用文件描述符filename
打开文件3
。 exec 3<&0 <filename
我在#2中描述过。
我猜这些老贝壳有效地执行了
while ...; do ... ; done < filename
作为
cat filename | while ...
从而在子shell中执行while
循环。
事先使用那些繁琐的exec
命令进行重定向,避免了while
块的重定向,从而避免了隐式子shell的重定向。
然而,我从来没有听说过那种奇怪的行为,我猜你除非你正在使用真正古老的贝壳,否则你不会处理它。