Как найти следующий доступный файловый дескриптор в Bash?
Как я могу узнать, используется ли дескриптор файла в Bash? Например, если у меня есть скрипт, который читает, пишет и закрывает fd 3, например
exec 3< <(some command here)
...
cat <&3
exec 3>&-
Каков наилучший способ убедиться, что я не вмешиваюсь в какую-либо другую цель для дескриптора, который мог быть установлен до запуска моего скрипта? Нужно ли помещать весь сценарий в подоболочку?
3 ответа
В чистом виде 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, если это ОС, на которую вы ориентируетесь.
Конечно, у вас все еще есть возможность обернуть весь сценарий оболочки в нечто вроде:
(
# Your current script goes here
)
В этом случае файловые дескрипторы будут сохранены вне этих скобок, и вы можете манипулировать ими внутри по своему усмотрению.
Если вам все равно, если дескриптор файла больше 9, вы можете попросить его предоставить самой оболочке. Конечно, fd гарантированно будет свободным от собственной оболочки.
Функция доступна с 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
Другой ответ, который использует синтаксис pre-bash-4.1, делает много ненужных порождений подоболочек и избыточных проверок. У этого также есть произвольное сокращение для максимального числа FD.
Следующий код должен сделать трюк без порождения подоболочки (кроме как для ulimit
звоните, если мы хотим получить приличный верхний предел для номеров FD).
fd=2 max=$(ulimit -n)
while ((++fd < max)); do
! true <&$fd && break
done 2>/dev/null && echo $fd
- По сути, мы просто перебираем возможные FD, пока не дойдем до того, что не сможем обмануть
- Чтобы избежать
Bad file descriptor
сообщение об ошибке из последней итерации цикла, мы перенаправим stderr для всегоwhile
петля.
Для тех, кто предпочитает однострочников и не имеет Bash-4.1+ доступны:
{ seq 0 255; ls -1 /proc/$$/fd; } | sort -n | uniq -u | head -1
Я решил суммировать блестящий ответ, данный @paxdiablo, в одной функции оболочки с двумя вспомогательными:
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) - используются только встроенные функции