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

时间:2017-01-12 01:42:06

标签: bash file-descriptor subshell process-substitution

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

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

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

5 个答案:

答案 0 :(得分:10)

如果你不关心文件描述符是否高于9,你可以要求shell本身提供一个。当然,fd保证由自己的shell自由。

自bash 4.1+(2009-12-31){varname} style automatic file descriptor allocation

以来可用的功能
$ 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中,你可以看到open fds:

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

答案 1 :(得分:7)

在纯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
)

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

答案 2 :(得分:7)

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

如果我们想要在FD数字上得到一个合适的上限,下面的代码应该没有子shell产生(除ulimit调用之外)。

fd=2 max=$(ulimit -n)

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

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

我决定将@paxdiablo给出的绝妙答案总结为具有两个辅助函数的单壳函数:

message.getSubject().indexOf('END OF DAY SALES DETAILS') !== -1

有一些简化-从辅助功能中逃脱而不会丢失主要功能:

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 } 和对特定FD的读/写)
  • 仅使用内建