文件描述符

有人能告诉我为什么这不起作用吗? 我正在玩文件描述符,但感觉有点失落。

#!/bin/bash echo "This" echo "is" >&2 echo "a" >&3 echo "test." >&4 

前三行运行良好,但最后两个错误。 为什么? 谢谢您的帮助!

文件描述符0,1和2分别用于stdin,stdout和stderr。

文件描述符3,4,… 9用于附加文件。 为了使用它们,您需要先打开它们。 例如:

 exec 3<> /tmp/foo #open fd 3. echo "test" >&3 exec 3>&- #close fd 3. 

有关更多信息,请参阅高级Bash脚本指南:第20章I / Oredirect 。

这是一个古老的问题,但有一件事需要澄清

虽然卡尔诺鲁姆和dogbane的答案是正确的,假设是改变你的脚本,使其工作

我想指出的是, 你不需要改变脚本

 #!/bin/bash echo "This" echo "is" >&2 echo "a" >&3 echo "test." >&4 

如果以不同的方式调用它,它将起作用:

 ./fdtest 3>&1 4>&1 

这意味着将文件描述符3和4redirect到1(这是标准输出)。

重点在于, 如果这些描述符是由父进程提供的那么脚本完全可以写入除1和2以外的描述符(stdout和stderr)。

你的例子其实很有趣,因为这个脚本可以写入4个不同的文件:

 ./fdtest >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt 

现在你有4个单独的文件输出:

 $ for f in file*; do echo $f:; cat $f; done file1.txt: This file2.txt: is file3.txt: a file4.txt: test. 

更有意思的是,你的程序不必为这些文件拥有写权限,因为它实际上并没有打开它们。

例如,当我运行sudo -s将用户更改为root用户时,以root用户身份创build一个目录,并尝试以普通用户身份运行以下命令(在我的情况下为rsp),如下所示:

 # su rsp -c '../fdtest >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt' 

我收到一个错误:

 bash: file1.txt: Permission denied 

但是如果我在su之外进行redirect,

 # su rsp -c '../fdtest' >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt 

(注意单引号的区别) 它的工作原理和我得到:

 # ls -alp total 56 drwxr-xr-x 2 root root 4096 Jun 23 15:05 ./ drwxrwxr-x 3 rsp rsp 4096 Jun 23 15:01 ../ -rw-r--r-- 1 root root 5 Jun 23 15:05 file1.txt -rw-r--r-- 1 root root 39 Jun 23 15:05 file2.txt -rw-r--r-- 1 root root 2 Jun 23 15:05 file3.txt -rw-r--r-- 1 root root 6 Jun 23 15:05 file4.txt 

即root拥有的根目录中拥有的4个文件 – 即使该脚本没有创build这些文件的权限

另一个例子是使用chroot jail或者一个容器,然后在内部运行一个程序,即使它以root身份运行,仍然无法访问这些文件,并且仍然在需要时从外部redirect这些描述符,而无法实际访问整个文件系统或其他任何东西到这个脚本。

关键是你已经发现了一个非常有趣和有用的机制 。 您不必像其他答案中所build议的那样打开脚本中的所有文件。 有时在脚本调用期间redirect它们是有用的。

总结起来 ,这个:

 echo "This" 

其实相当于:

 echo "This" >&1 

并运行该程序为:

 ./program >file.txt 

是相同的:

 ./program 1>file.txt 

数字1只是一个默认的数字,它是标准输出。

但即使这个程序:

 #!/bin/bash echo "This" 

可能会产生“错误的描述符”错误。 怎么样? 当运行时:

 ./fdtest2 >&- 

输出将是:

 ./fdtest2: line 2: echo: write error: Bad file descriptor 

添加>&- (与1>&-相同)意味着closures标准输出。 添加2>&-将意味着closuresstderr。

你甚至可以做一个更复杂的事情 。 您的原始脚本:

 #!/bin/bash echo "This" echo "is" >&2 echo "a" >&3 echo "test." >&4 

当运行只是:

 ./fdtest 

打印:

 This is ./fdtest: line 4: 3: Bad file descriptor ./fdtest: line 5: 4: Bad file descriptor 

但是你可以使描述符3和4工作,但是1号运行失败:

 ./fdtest 3>&1 4>&1 1>&- 

它输出:

 ./fdtest: line 2: echo: write error: Bad file descriptor is a test. 

如果你想要描述符1和2都失败,就像这样运行它:

 ./fdtest 3>&1 4>&1 1>&- 2>&- 

你得到:

 a test. 

为什么? 没有任何失败? 它做了,但没有标准错误(文件描述符编号2), 你没有看到错误信息!

我认为这样做是非常有用的,以便了解描述符及其redirect如何工作。

你的脚本确实是一个非常有趣的例子 – 我认为它并没有被打破,你只是错误地使用它! 🙂

这是失败的,因为这些文件描述符不指向任何东西! 正常的默认文件描述符是标准input0 ,标准输出1和标准错误stream2 。 由于您的脚本不打开任何其他文件,因此没有其他有效的文件描述符。 你可以使用exec在bash中打开一个文件。 这是你的例子的一个修改:

 #!/bin/bash exec 3> out1 # open file 'out1' for writing, assign to fd 3 exec 4> out2 # open file 'out2' for writing, assign to fd 4 echo "This" # output to fd 1 (stdout) echo "is" >&2 # output to fd 2 (stderr) echo "a" >&3 # output to fd 3 echo "test." >&4 # output to fd 4 

现在我们将运行它:

 $ ls script $ ./script This is $ ls out1 out2 script $ cat out* a test. $ 

正如你所看到的,额外的输出被发送到请求的文件。