在Bash脚本中,是否可以在"尚未使用的编号最小的文件描述符"上打开文件?
我四处寻找如何做到这一点,但似乎Bash总是要求你指定数字,例如:
exec 3< /path/to/a/file # Open file for reading on file descriptor 3.
Run Code Online (Sandbox Code Playgroud)
相反,我希望能够做类似的事情
my_file_descriptor=$(open_r /path/to/a/file)
Run Code Online (Sandbox Code Playgroud)
这将打开'file'以读取尚未使用的编号最小的文件描述符,并将该编号分配给变量'my_file_descriptor'.
小智 63
我知道这个帖子已经过时了,但是相信最好的答案是缺失的,对于像我这样来寻找解决方案的人来说会很有用.
Bash和Zsh已经内置了查找未使用的文件描述符的方法,而无需编写脚本.(我发现破折号没有这样的东西,所以上面的答案可能仍然有用.)
注意:这会找到最低的未使用文件描述符> 10,而不是总体最低值.
$ man bash /^REDIRECTION (paragraph 2)
$ man zshmisc /^OPENING FILE DESCRIPTORS
Run Code Online (Sandbox Code Playgroud)
示例适用于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
Run Code Online (Sandbox Code Playgroud)
完成后关闭文件描述符:
$ exec {FD}>&-
Run Code Online (Sandbox Code Playgroud)
以下显示文件描述符现已关闭:
$ echo line 3 >&$FD
bash: $FD: Bad file descriptor
zsh: 10: bad file descriptor
Run Code Online (Sandbox Code Playgroud)
我修改了原始答案,现在为原始帖子提供了一种解决方案。
以下函数可以存在于全局文件或源脚本中(例如〜/ .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 (i.e. '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;
}
Run Code Online (Sandbox Code Playgroud)
可以使用以下功能打开文件进行输入和输出:
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'
Run Code Online (Sandbox Code Playgroud)
然后,可以像往常一样使用前面的描述符来读写数据:
read -u $inputFile data
echo "input file contains data \"$data\"" >&$log
Run Code Online (Sandbox Code Playgroud)
我需要支持双方在Mac庆典v3和v4的bash的在Linux和其他解决方案需要既庆典V4或Linux,所以我想出了一个解决方案,两个作品,使用/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
}
Run Code Online (Sandbox Code Playgroud)
例如复制标准输出,你可以这样做:
newfd=$(find_unused_fd)
eval "exec $newfd>&1"
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6965 次 |
| 最近记录: |