在Bash中,如何find编号最小的未使用的文件描述符?
在Bash脚本中,是否可以在“最低编号的文件描述符尚未使用”上打开文件?
我已经四处寻找如何做到这一点,但似乎Bash总是要求你指定数字,例如像这样:
exec 3< /path/to/a/file # Open file for reading on file descriptor 3.
相反,我想能够做到这样的事情
my_file_descriptor=$(open_r /path/to/a/file)
这将打开“文件”来读取尚未使用的编号最小的文件描述符,并将该编号分配给variables“my_file_descriptor”。
我知道这个post已经老了,但是相信最好的答案是不存在的,对于像我这样的人来说,他们会寻求解决scheme。
Bash和Zsh构build了查找未使用的文件描述符的方法,而不必编写脚本。 (我没有发现破折号,所以上面的答案可能仍然有用。)
注意:这find最低的未使用的文件描述符> 10,而不是最低的整体。
$ man bash /^REDIRECTION (paragraph 2) $ man zshmisc /^OPENING FILE DESCRIPTORS
示例适用于bsh和zsh。
打开一个未使用的文件描述符,并将编号分配给$ FD:
$ exec {FD}>test.txt $ echo line 1 >&$FD $ echo line 2 >&$FD $ cat test.txt line 1 line 2 $ echo $FD 10 # this number will vary
完成时closures文件描述符:
$ exec {FD}>&-
以下显示文件描述符现在已closures:
$ echo line 3 >&$FD bash: $FD: Bad file descriptor zsh: 10: bad file descriptor
如果是在Linux上,你总是可以读取/proc/self/fd/
目录来找出使用的文件描述符。
我修改了我原来的答案,现在有一个原始post的解决scheme。
下面的函数可以存在于一个全局文件或源文件中(例如〜/ .bashrc):
# Some error code mappings from errno.h readonly EINVAL=22 # Invalid argument readonly EMFILE=24 # Too many open files # Finds the lowest available file descriptor, opens the specified file with the descriptor # and sets the specified variable's value to the file descriptor. If no file descriptors # are available the variable will receive the value -1 and the function will return EMFILE. # # Arguments: # The file to open (must exist for read operations) # The mode to use for opening the file (ie 'read', 'overwrite', 'append', 'rw'; default: 'read') # The global variable to set with the file descriptor (must be a valid variable name) function openNextFd { if [ $# -lt 1 ]; then echo "${FUNCNAME[0]} requires a path to the file you wish to open" >&2 return $EINVAL fi local file="$1" local mode="$2" local var="$3" # Validate the file path and accessibility if [[ "${mode:='read'}" == 'read' ]]; then if ! [ -r "$file" ]; then echo "\"$file\" does not exist; cannot open it for read access" >&2 return $EINVAL fi elif [[ !(-w "$file") && ((-e "$file") || !(-d $(dirname "$file"))) ]]; then echo "Either \"$file\" is not writable (and exists) or the path is invalid" >&2 return $EINVAL fi # Translate mode into its redirector (this layer of indirection prevents executing arbitrary code in the eval below) case "$mode" in 'read') mode='<' ;; 'overwrite') mode='>' ;; 'append') mode='>>' ;; 'rw') mode='<>' ;; *) echo "${FUNCNAME[0]} does not support the specified file access mode \"$mode\"" >&2 return $EINVAL ;; esac # Validate the variable name if ! [[ "$var" =~ [a-zA-Z_][a-zA-Z0-9_]* ]]; then echo "Invalid variable name \"$var\" passed to ${FUNCNAME[0]}" >&2 return $EINVAL fi # we'll start with 3 since 0..2 are mapped to standard in, out, and error respectively local fd=3 # we'll get the upperbound from bash's ulimit local fd_MAX=$(ulimit -n) while [[ $fd -le $fd_MAX && -e /proc/$$/fd/$fd ]]; do ((++fd)) done if [ $fd -gt $fd_MAX ]; then echo "Could not find available file descriptor" >&2 $fd=-1 success=$EMFILE else eval "exec ${fd}${mode} \"$file\"" local success=$? if ! [ $success ]; then echo "Could not open \"$file\" in \"$mode\" mode; error: $success" >&2 fd=-1 fi fi eval "$var=$fd" return $success; }
用户可以使用上述function打开input输出文件:
openNextFd "path/to/some/file" "read" "inputfile" # opens 'path/to/some/file' for read access and stores # the descriptor in 'inputfile' openNextFd "path/to/other/file" "overwrite" "log" # truncates 'path/to/other/file', opens it in write mode, and # stores the descriptor in 'log'
然后像往常一样使用前面的描述符来读取和写入数据:
read -u $inputFile data echo "input file contains data \"$data\"" >&$log
在Basile Starynkevitch对这个问题的回答中,他在2011年11月29日写道:
如果是在Linux上,你总是可以读取/ proc / self / fd /目录来找出使用的文件描述符。
根据阅读fd目录做了几个实验,我得到了下面的代码,作为“最匹配”,我正在寻找。 我正在寻找的实际上是一个bash单线,就像
my_file_descriptor=$(open_r /path/to/a/file)
这将find最低的,未使用的文件描述符并打开文件,并将其分配给variables。 如下面的代码所示,通过引入函数“lowest_unused_fd”,我至less得到了任务“双线”(FD = $(lowest_unused_fd), 然后是 eval“exec $ FD <$ FILENAME”)。 我一直没能写出像上面(虚构的)“open_r”那样的函数。 如果有人知道如何做到这一点,请继续前进! 相反,我必须将任务分成两步:一步find未使用的文件描述符,一步打开文件。 还要注意,为了能够将查找步骤放在一个函数(“lowest_unused_fd”)中并将其stdout分配给FD,我必须使用“/ proc / $$ / fd”而不是“/ proc / self / fd “(如在Basile Starynkevitch的build议中),因为bash产生了执行该函数的子shell。
#!/bin/bash lowest_unused_fd () { local FD=0 while [ -e /proc/$$/fd/$FD ]; do FD=$((FD+1)) done echo $FD } FILENAME="/path/to/file" # Find the lowest, unused file descriptor #+ and assign it to FD. FD=$(lowest_unused_fd) # Open the file on file descriptor FD. if ! eval "exec $FD<$FILENAME"; then exit 1 fi # Read all lines from FD. while read -u $FD a_line; do echo "Read \"$a_line\"." done # Close FD. eval "exec $FD<&-"
我需要在Mac上支持bash v3,在Linux上支持bash v4,其他解决scheme需要bash v4或者Linux,所以我想出了一个适用于两者的解决scheme,使用/dev/fd
。
find_unused_fd() { local max_fd=$(ulimit -n) local used_fds=" $(/bin/ls -1 /dev/fd | sed 's/.*\///' | tr '\012\015' ' ') " local i=0 while [[ $i -lt $max_fd ]]; do if [[ ! $used_fds =~ " $i " ]]; then echo "$i" break fi (( i = i + 1 )) done }
例如要复制stdout,你可以这样做:
newfd=$(find_unused_fd) eval "exec $newfd>&1"