如何在Bash中找到下一个可用的文件描述符



如何判断文件描述符当前是否在Bash中使用?例如,如果我有一个读取、写入和关闭fd 3的脚本,例如

exec 3< <(some command here)
...
cat <&3
exec 3>&-

确保我不会干扰描述符的其他用途的最佳方法是什么?描述符可能在脚本运行之前设置好了?我需要把我的整个剧本放在一个潜艇里吗?

如果您不关心文件描述符是否高于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.

事实上,在linux中,您可以看到打开的fds带有:

$ ls /proc/$$/fd
0 1 2 255

在纯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(BDS和CygWin就是示例)。如果Linux是您的目标操作系统,那么它应该是不错的。


当然,您仍然可以将整个shell脚本包装为

(
# Your current script goes here
)

在这种情况下,文件句柄将保留在这些括号之外,您可以根据需要在其中操作它们。

另一个使用pre-bash-4.1语法的答案做了很多不必要的子shell生成和冗余检查。它还具有最大FD数的任意截止值。

下面的代码应该在没有子shell生成的情况下完成任务(除了ulimit调用,如果我们想获得FD数的适当上限)。

fd=2 max=$(ulimit -n) &&
while ((++fd < max)); do
! <&$fd && break
done 2>/dev/null &&
echo $fd
  • 基本上,我们只是对可能的FD进行迭代,直到达到一个我们无法欺骗的FD
  • 为了避免上一次循环迭代中出现Bad file descriptor错误消息,我们为整个while循环重定向stderr

对于那些喜欢一行并且没有可用的Bash-4.1+的人:

{ seq 0 255; ls -1 /proc/$$/fd; } | sort -n | uniq -u | head -1

我决定将@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和R/W到特定FD)
  • 仅使用内置

相关内容

  • 没有找到相关文章

最新更新