11

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

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

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

4

5 回答 5

24

如果您不关心文件描述符是否高于 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
于 2017-01-12T18:42:13.477 回答
8

在 purebash中,您可以使用以下方法查看给定的文件描述符(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
)

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

于 2017-01-12T01:54:34.870 回答
7

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

下面的代码应该在不产生子shell的情况下完成这个技巧(ulimit如果我们想获得一个体面的 FD 号码上限,则可以用于调用)。

fd=2 max=$(ulimit -n)

while ((++fd < max)); do
   ! true <&$fd && break
done 2>/dev/null && echo $fd
  • 基本上,我们只是迭代可能的 FD,直到我们找到一个我们无法欺骗的 FD。
  • 为了避免Bad file descriptor上次循环迭代的错误消息,我们为整个while循环重定向 stderr。
于 2017-01-13T02:10:09.287 回答
1

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

{ seq 0 255; ls -1 /proc/$$/fd; } | sort -n | uniq -u | head -1
于 2019-04-03T18:21:14.457 回答
0

我决定将@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)
  • 仅使用内置函数
于 2019-11-19T10:59:28.537 回答