有人可以告诉我为什么这行不通吗?我在玩文件描述符,但是有点迷路。
#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4
前三行运行正常,但后两行出错。为什么?
Answers:
文件描述符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 / O重定向》。
exec 3>&1
的原因echo hi >&3
打印“嗨”到标准输出。
3<>
不只是3>
在第一行?
这是一个老问题,但有一件事需要澄清。
尽管Carl Norum和Dogbane的答案是正确的,但前提是要更改脚本以使其起作用。
我想指出的是,您不需要更改脚本:
#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4
如果您以其他方式调用它,它将起作用:
./fdtest 3>&1 4>&1
这意味着将文件描述符3和4重定向到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用户身份创建目录,然后尝试以我的普通用户(在我的情况下为rsp)运行以下命令:
# su rsp -c '../fdtest >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt'
我收到一个错误:
bash: file1.txt: Permission denied
但是如果我在以下之外进行重定向su
:
# 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拥有的目录中root拥有的4个文件-即使该脚本没有创建这些文件的权限。
另一个例子是使用chroot jail或容器,并在其中运行程序,即使该程序以root身份运行,也无法访问这些文件,并且仍然在需要的地方将这些描述符重定向到外部,而实际上并未授予对整个文件的访问权限系统或此脚本的任何其他内容。
关键是您发现了一种非常有趣且有用的机制。您不必按照其他答案中的建议打开脚本中的所有文件。有时在脚本调用期间重定向它们很有用。
总结一下:
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>&-
)表示关闭标准输出。添加2>&-
将意味着关闭stderr。
您甚至可以做更复杂的事情。您的原始脚本:
#!/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.
为什么?没什么失败吗?它可以,但是没有stderr(文件描述符号2),您没有看到错误消息!
我认为以这种方式进行试验以了解描述符及其重定向的工作方式非常有用。
您的脚本确实是一个非常有趣的示例-我认为它根本没有损坏,您只是在错误地使用它!:)
./fdtest
没有3>&1 4>&1
,有没有一种方法fdtest
来确定文件描述符3和4不存在?
之所以失败,是因为这些文件描述符没有指向任何东西!普通的默认文件描述符是标准输入0
,标准输出1
和标准错误流2
。由于您的脚本不会打开任何其他文件,因此没有其他有效的文件描述符。您可以使用来打开bash文件exec
。这是您的示例的修改:
#!/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.
$
如您所见,多余的输出已发送到请求的文件。
stdout
或stderr
。您为什么需要或想要使用其他文件?
要在添加到从RSP回答和回应的评论的问题,从这个问题的答案的@MattClimbs。
您可以尝试尽早重定向到文件描述符,以测试文件描述符是否打开,如果失败,则将所需编号的文件描述符打开为/dev/null
。我会定期在脚本中执行此操作,并利用其他文件描述符将其他详细信息或响应传递回return #
。
#!/bin/bash
2>/dev/null >&3 || exec 3>/dev/null
2>/dev/null >&4 || exec 4>/dev/null
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4
将stderr重定向到,/dev/null
以丢弃可能的bash: #: Bad file descriptor
响应,并且当前一个以非零状态退出时,||
使用stderr处理以下命令exec #>/dev/null
。如果文件描述符已经打开,则这两个测试将返回零状态,并且该exec ...
命令将不被执行。
调用脚本而不进行任何重定向将产生:
# ./script.sh
This
is
在这种情况下,对于重定向a
和test
被运到/dev/null
使用定义的重定向调用脚本会产生:
# ./script.sh 3>temp.txt 4>>temp.txt
This
is
# cat temp.txt
a
test.
第一次重定向将3>temp.txt
覆盖文件,temp.txt
同时4>>temp.txt
追加到文件。
最后,如果您需要其他内容,则可以定义默认文件以重定向到脚本内,/dev/null
或者可以更改脚本的执行方法,并将这些多余的文件描述符重定向到所需的任何位置。