Тестування, чи правильний дескриптор файлу


12

Я хотів би зробити виведення сценарію bash додаткової інформації для дескрипторів файлів (FD), більших або рівних 3, коли вони відкриті. Щоб перевірити, чи відкритий FD, я створив наступний трюк:

if (printf '' 1>&3) 2>&-; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi

Цього достатньо для моїх потреб, але мені цікаво, чи існує більш ідіоматичний спосіб тестування, якщо FD дійсний. Я особливо зацікавлений про те, чи існує відображення системного fcntl(1)виклику до команди оболонки, яка допускала б витяг FD прапорів ( O_WRONLYі O_RDWRдля тестування чи FD перезаписує, а O_RDONLYй O_RDWRдля тестування чи FD читається).

Відповіді:


12

У ksh(як AT&T, так і pdksh варіантах) або zsh, ви можете:

if print -nu3; then
  echo fd 3 is writeable
fi

Вони нічого не пишуть на цьому fd, але все ж перевірять, чи fd написано (використовуючи fcntl(3, F_GETFL)), і повідомляють про помилку в іншому випадку:

$ ksh -c 'print -nu3' 3< /dev/null
ksh: print: -u: 3: fd not open for writing

(на яку можна переспрямувати /dev/null).

З bash, я думаю, ваш єдиний варіант - перевірити, чи є такий dup()успіх, як у вашому підході, хоча це не гарантує, що fd можна записати (або викликати зовнішню утиліту ( zsh/ perl...), щоб зробити це fcntl()).

Зауважте, що bash(як і більшість оболонок), якщо ви використовуєте (...)замість цього {...;}, це призведе до додаткового процесу. Ви можете використовувати:

if { true >&3; } 2<> /dev/null

натомість, щоб уникнути вилки (за винятком оболонки Bourne, де перенаправлення складених команд завжди спричиняє підзарядку). Не використовуйте :замість цього, trueоскільки це спеціальний вбудований модуль, що може спричинити вихід оболонки, коли bash знаходиться в режимі відповідності POSIX.

Однак ви можете скоротити його до:

if { >&3; } 2<> /dev/null

@mikeserve, re: твоя редакція, що з цим <>? Оболонка не збирається читати з її складніше, чому б ви хотіли відкрити її в режимі read + write? Що ви маєте на увазі з тим, що сталося із внутрішньою? ?
Стефан Шазелас

7

В описі використання додатка POSIX ви знайдете наступне:command

Є певні переваги придушення особливих характеристик спеціальних вбудованих випадків. Наприклад:

command exec > unwritable-file

не спричиняє переривання неінтерактивного скрипту, так що стан виводу можна перевірити сценарієм.

Ось чому ви можете просто зробити:

if    command >&3
then  echo 3 is open >&3
else  ! echo 3 is not open
fi    2<>/dev/null

Або ...

{ command >&3
  printf %s\\n%.0d  string "0$(($??8:0))" >&"$(($??1:3))"
} 2<>/dev/null

Який буде писати рядок з наступною \newline або stdout, або 3, і все ще передаватиме ненульовий статус виходу, коли 3 не відкрито, оскільки математика, виконана на $?вітрі, не зможе перетворити восьмеричний 08 у % десятковий, але скорочується зовсім ні до чого восьмеричний 00 .

Або ...

command exec >&3 || handle_it

Але якщо ви використовуєте ksh93, ви можете просто зробити:

fds

Список відкритих дескрипторів файлів. Додати, -lщоб побачити, куди вони йдуть.


3

Дескриптори відкритих файлів можна знайти в /proc/<pid>/fd. Наприклад, перелічити, наприклад, дескриптори відкритого файлу поточної оболонки, які ви можете видавати, ls -l /proc/$$/fdякі повинні дати вам щось на зразок:

total 0
lrwx------ 1 testuser testuser 64 jun  1 09:11 0 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:11 1 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:11 2 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:39 255 -> /dev/pts/3

Коли ви відкриваєте файл, використовуючи:

touch /tmp/myfile
exec 7</tmp/myfile

Він повинен бути перерахований новим ls -l /proc/$$/fd:

lr-x------ 1 testuser testuser 64 jun  1 09:11 7 -> /tmp/myfile

Якщо ви знову закриєте дескриптор файлу, використовуючи exec 7>&-його, він також більше не вказаний /proc/$$/fd.


2
Все це досить специфічно для Linux. FWIW.
lcd047

1
Тестували його на Linux, а також на Solaris (10 та 11). Різниця полягає в тому, що вам потрібно скористатися, pfiles <pid>щоб побачити, який дескриптор файлу підключений до якого файлу, ls -lвідображаючи з'єднання в Linux.
Ламберт

Мені подобається компактність [ -e /proc/$$/fd/3 ], але я вважаю за краще не покладатися на profs, оскільки це застаріло у FreeBSD і, можливо, також інших un * ces.
Вітіко

1
Підводить мене до альтернативи використання pfiles <pid>чи lsof -p <pid>перегляду дескрипторів файлів, які відкриті.
Ламберт

1
/procвзагалі не існує на OpenBSD. У FreeBSD та NetBSD це має бути mountявно -ed, і /proc/<PID>не мати підкаталога fd.
lcd047

3

Ваш трюк виглядає мило; але для ідіоматичного способу я цікавлюсь, чому ви не використовували:

if ( exec 1>&3 ) 2>&-

Це, дійсно, більш чистий спосіб.
Вітіко

5
Це створює додаткову оболонку, хоча це більшість оболонок, означає форсування процесу. Це не гарантує, що fd можна записати. Ви можете використовувати, { true >&3; } 2> /dev/nullщоб уникнути вилки. Або { command exec >&3; } 2> /dev/nullякщо ви хочете перенаправити stdout на нього.
Stéphane Chazelas

@ Стефан; Трюк підзарядки, який вигадав @Witiko, полягав у тому, щоб не впливати на дескриптори файлів поточного середовища при використанні перенаправлення для отримання перенаправлення. - Не могли б ви детальніше розглянути « згадуваний фд», про який ви згадали?
Janis

2
{ true >&3; } 2> /dev/nullне вплине і на поточне середовище, і не буде розщедритися (за винятком оболонки Борна). Я маю на увазі, що (exec 1>&3) 2>&-повернеться true для fd, відкритого в режимі лише для читання.
Stéphane Chazelas

1
execбудучи спеціальним вбудованим, він вийде з оболонки, якщо вона не виходить (для bash, лише в режимі відповідності POSIX). command execперешкоджає цьому. trueне є особливим вбудованим. Зауважте, що execі command execвпливає на поточне середовище (саме тому я сказав, якщо ви хочете перенаправити stdout на нього ).
Stéphane Chazelas

-1

Якщо вас зацікавило рішення з низьким розміщенням, щоб повторно використовувати його, я б запропонував цю функцію:

checkfd () {
    exec 2> / dev / null
    якщо exec> & 3; тоді
        exec 1> / dev / tty
        ехо "fd3 ОК"
    ще
        відлуння "fd3 KO"
    фі
    exec 2> / dev / tty
}

І ось що він виробляє з zsh:

$ checkfd            
fd3 KO
$ checkfd 3> / dev / null
fd3 Гаразд
$

У більшості оболонок exec >&3буде вбита оболонка, коли 3 не відкрито.
mikeserv

Принаймні, це працює над zshі bash. Не могли б ви надати оболонку, на якій execстався збій exit?
дан

Так. В bashробити set -o posixі спробуйте ще раз. У zsh... я думаю, що це питання встановлення env var POSIX_BUILTINSна ненулеве значення - але я забуваю назовні. У будь-якому випадку, zshце не оболонка, яка намагається відповідати POSIX, і це, безумовно, нестандартно. Обидві ці оболонки уникають сумісності, оскільки деякі вважають, що це зручність.
mikeserv

Він також працює на звичайній оболонці Борна.
дан

У баші, set -o posixспробуй успішно.
дан

-1

Це здається дуже просто (див. Коментарі):

[ -r /proc/$$/fd/$FD ] && echo "File descriptor $FD is readable"
[ -w /proc/$$/fd/$FD ] && echo "File descriptor $FD is writable"

Як додатковий ... Тест [-r file] не вказує, чи дійсно якісь дані очікують на зчитування (/ dev / null проходить цей тест (див. Коментарі)).

[ -r /proc/$$/fd/4 ] \
  && [ read -t 0.0001 -N 0 <&4 ] \
  && echo "Data is waiting to be read from file descriptor 4"

Потрібно деяке невелике число для аргументу тайм-аута (read -t) або дані, які потребують певного обчислення, можуть бути пропущені. Необхідний тест для читання ([-r файл]), або команда читання спрацює, якщо файл не читається. Це фактично не буде читати жодних даних, оскільки кількість байтів дорівнює нулю (read -N 0).


якщо ви збираєтеся взяти на себе систему Linux, ви також можете також ознайомитись із /proc/<pid>/fdinfo/<fd>переліком усіх відкритих файлів у режимі flags:- дивіться тут . Чому ваша друга частина (навіть після виправлення очевидної помилки): read -t .1 -N0 <&4не скаже, чи є дані, які слід прочитати на fd 4: просто спробуйте 4</dev/null.
mosvy

І звичайно, [ -r /proc/$$/fd/$FD ]не розповідає, чи $FDчитається дескриптор файлу , але якщо файл, з якого він був відкритий, можна було б відкрити знову , з іншим дескриптором файлів, для читання:exec 7>/tmp/foo; [ -r /proc/$$/fd/7 ] && echo fd 7 can be read from && cat <&7
mosvy

-1

Питання досить старе - але все одно - чому просто не використовувати вбудовані?

for i in {0..5} ; do if [ -t $i ]; then echo "$i is a valid FD"; else echo "$i is INVALID FD"; fi; done

Вихід:

0 is a valid FD
1 is a valid FD
2 is a valid FD
3 is INVALID FD
4 is INVALID FD
5 is INVALID FD

Отже, щоб відповісти на питання - запропонував би:

if [ -t 3 ]; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi

-tне перевіряє, чи правильний дескриптор файлу, але якщо він підключений до tty. Додайте echo yup |до свого сценарію і скаже, що 0 is INVALID FD, хоча насправді це справді fd, труба.
mosvy
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.