如何在Bash中判断文件描述符是否正在使用?例如,如果我有一个读取、写入和关闭fd 3的脚本。
exec 3< <(some command here)
...
cat <&3
exec 3>&-
如何最好地确保我的脚本不会干扰在我脚本运行之前可能已经设置的描述符的其他用途?我需要把整个脚本放在子shell中吗?
如何在Bash中判断文件描述符是否正在使用?例如,如果我有一个读取、写入和关闭fd 3的脚本。
exec 3< <(some command here)
...
cat <&3
exec 3>&-
如何最好地确保我的脚本不会干扰在我脚本运行之前可能已经设置的描述符的其他用途?我需要把整个脚本放在子shell中吗?
如果您不关心文件描述符是否大于9,您可以请求shell本身提供一个。当然,fd保证由自己的shell释放。
自bash 4.1+(2009-12-31)以来可用的功能 {varname}样式自动文件描述符分配
$ exec {var}>hellofile
$ echo "$var"
15
$ echo "this is a test" >&${var}
$ cat hellofile
this is a test
$ exec {var}>&- # to close the fd.
$ ls /proc/$$/fd
0 1 2 255
exec {my_var} > hellofile
,你会得到那个错误。注意大于号前面的空格。 - wjandrea在纯粹的bash
中,您可以使用以下方法检查给定文件描述符(3
在这种情况下)是否可用:
rco="$(true 2>/dev/null >&3; echo $?)"
rci="$(true 2>/dev/null <&3; echo $?)"
if [[ "${rco}${rci}" = "11" ]] ; then
echo "Cannot read or write fd 3, hence okay to use"
fi
exec 3>/dev/null # Testing, comment out to make
exec 4</dev/null # descriptor available.
found=none
for fd in {0..200}; do
rco="$(true 2>/dev/null >&${fd}; echo $?)"
rci="$(true 2>/dev/null <&${fd}; echo $?)"
[[ "${rco}${rci}" = "11" ]] && found=${fd} && break
done
echo "First free is ${found}"
运行该脚本将返回第一个可用的描述符为5
,但您可以尝试修改exec
行以查看如何使更早的描述符可用,从而使代码片段能够找到它。
正如评论中指出的那样,提供procfs
(即/proc
文件系统)的系统还有另一种检测空闲描述符的方法。每个打开的文件描述符都将在/proc/PID/fd
目录中包含一个条目,如下所示:
pax> ls -1 /proc/$$/fd
0
1
2
255
exec 3>/dev/null # Testing, comment out to make
exec 4</dev/null # descriptor available.
found=none
for fd in {0..200} ; do
[[ ! -e /proc/$$/fd/${fd} ]] && found=${fd} && break
done
echo "First free is ${found}"
请注意,并非所有提供 bash
的系统都一定具有 procfs
(例如 BDSs 和 CygWin)。如果您的目标是 Linux,那么应该没问题。
当然,您仍然可以选择将整个 shell 脚本包装成以下内容:
(
# Your current script goes here
)
ls /proc/$$/fd
列表中未提供的数字有什么问题吗? - user8017719procfs
,那可能是一个选项,但它并不普遍存在(例如BSD)。鉴于问题仅提到了bash
,我认为提供一个可移植的解决方案更好。我会记下您的建议。 - paxdiablo ulimit 调用以获取FD号码的合理上限)。
fd=2 max=$(ulimit -n) &&
while ((++fd < max)); do
! <&$fd && break
done 2>/dev/null &&
echo $fd
- 基本上我们只是迭代可能的FD,直到我们到达一个无法复制的FD。
- 为了避免最后一次循环迭代出现“坏文件描述符”错误消息,我们将整个
while
循环的stderr重定向。
对于那些喜欢一行代码且没有Bash-4.1+的人:
{ seq 0 255; ls -1 /proc/$$/fd; } | sort -n | uniq -u | head -1
/proc
目录;特别是 OSX 不支持。 - dimo414我决定将@paxdiablo提供的精彩答案总结为一个带有两个辅助函数的单一shell函数:
fd_used_sym() {
[ -e "/proc/$$/fd/$1" ]
}
fd_used_rw() {
: 2>/dev/null >&$1 || : 2>/dev/null <&$1
}
fd_free() {
local fd_check
if [ -e "/proc/$$/fd" ]
then
fd_check=fd_used_sym
else
fd_check=fd_used_rw
fi
for n in {0..255}
do
eval $fd_check $n || {
echo "$n"
return
}
done
}
有一种简化方式——在不失去主要功能的情况下避免使用辅助函数:
fd_free() {
local fd_check
if [ -e "/proc/$$/fd" ]
then
fd_check='[ -e "/proc/$$/fd/$n" ]'
else
fd_check=': 2>/dev/null >&$n || : 2>/dev/null <&$n'
fi
for n in {0..255}
do
eval $fd_check || {
echo "$n"
return
}
done
}
这两个函数都检查文件描述符的可用性并输出第一个找到的空闲文件描述符的编号。其优点如下:
/proc/$$/fd/X
和对特定FD的读写)$$
,它将不会使用当前子shell的fd空间,而是使用父级的fd空间,从而导致错误(在多次调用和打开时)。考虑让函数设置一个变量而不是回显它,这将需要对其进行$()
调用。这将使它保持在相同的子shell空间中。fd_used_rw
跳过了一个“不可见的fd 15”(实际上是打开的,但在/proc中不显示。我没有解释)。尽管fd_used_sym
更快,但fd_used_rw
更安全。
[
或[[
的选项来测试“文件描述符是否打开”。最接近的方法是-t number
,它测试文件描述符是否打开并连接到终端。让脚本解释这些数字很麻烦;你不能使用>&$number
,所以你必须玩弄子shell之类的东西。编写一个测试给定文件描述符的程序不难,但生成用于使用任意数字的shell脚本可能会很棘手。因此,大多数人都不会费心去做。 - Jonathan Leffler