`exec n<& 0<<& 0< file`和`exec n

时间:2014-08-27 19:36:13

标签: linux shell unix while-loop exec

由于我是shell脚本的新手,exec命令总是让我感到困惑,在使用while循环探索此主题时,触发了以下4个问题:

  1. 以下语法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<&-
    
  2. 请详细说明exec n<&0 < file清晰

  3. 的操作
  4. exec n<&0 < file命令是否等同于exec n<file? (如果没有,那么两者之间有什么区别?)

  5. 我在 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
    
  6. 此脚本尝试计算为其指定的文件中的行数作为参数。

    在文件上执行此脚本

    $ 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循环内的变量值,然后在循环外使用该值

3 个答案:

答案 0 :(得分:1)

  1. 现代贝壳中的差别应该是什么(两者都应该是POSIX compatible),但有一些警告:
    • 有可能使用数千个独特的shell二进制文件,其中一些缺少常见功能或只是错误。
    • 只有第一个版本在交互式shell中才会按预期运行,因为一旦标准输入获得EOF,shell就会关闭,这将在完成阅读file后发生。
    • while循环在两种情况下从FD 0读取,如果shell支持exec重定向到<循环,则while无意义。要从FD 9中读取,您必须使用done <&9(POSIX)或read -u 9(在Bash中)。
  2. exec(在这种情况下,请参阅help exec / man exec)将跟随它的redirections应用于当前shell,并从左到右应用它们。例如,exec 9<&0 < file将FD 9指向FD 0(标准输入)当前指向的位置,有效地使FD 9成为FD 0的“副本”。之后file被发送到标准输入(均为FD) 0和9)。
  3. 在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
    
  4. 这是关于* nix shell的常见误解: Variables declared in subshells (such as created by while) are not available to the parent shell. 这是设计,而不是错误。这里和USE上的许多其他答案都是指这个。

答案 1 :(得分:1)

这么多问题......但是所有问题都是同一个问题的变体,所以我会继续......

没有命令的

exec用于在当前进程中进行重定向。也就是说,它会更改附加到不同文件描述符(FD)的文件。

问题#1

我认为应该是这样的。在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

做同样的事情,但不那么复杂。

问题#2

exec {n}<&0 < file

这些是重定向,它们从左到右执行。第一个n<&0执行dup(0)(请参阅man dup)并将结果新FD存储在变量n中。然后<file执行open("file"...)并将其分配给FD 0。

问题#3

没有。 exec {n}<file打开文件并将新FD分配给变量n,保持标准输入(FD 0)不变。

问题#4

我不知道旧版本的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)

让我无序地回答你的问题。

Q#2

命令exec n<&0 < file是无效的语法。可能n代表&#34;一些任意数字&#34;。那就是说,例如

exec 3<&0 < file

按顺序执行两个重定向:它将标准输入文件描述符(0)复制/复制为文件描述符3。接下来,它重定向&#34;文件描述符0从文件file中读取。

稍后,命令

exec 0<&3 3<&-

首先从保存的文件描述符3复制标准输入文件描述符,将标准输入重定向回其先前的源。然后它关闭文件描述符3,它的目的是备份最初的stdin

问题#1

实际上,这两个示例都是相同的:它们暂时将stdin重定向到while循环的范围内。

Q#3

Nope:exec 3<filename使用文件描述符filename打开文件3exec 3<&0 <filename我在#2中描述过。

Q#4

我猜这些老贝壳有效地执行了

while ...; do ... ; done < filename

作为

cat filename | while ...

从而在子shell中执行while循环。

事先使用那些繁琐的exec命令进行重定向,避免了while块的重定向,从而避免了隐式子shell的重定向。

然而,我从来没有听说过那种奇怪的行为,我猜你除非你正在使用真正古老的贝壳,否则你不会处理它。