когда бы вы использовали дополнительный файловый дескриптор?

<p>я знаю, что вы можете создать дескриптор файла и перенаправить вывод на него. например.</p><pre><code>exec 3<> /tmp/foo # open fd 3. echo a >&3 # write to it exec 3>&- # close fd 3. </code></pre><p>но вы можете сделать то же самое без дескриптора файла:</p><pre><code>file=/tmp/foo echo a > "$file" я ищу хороший пример того, когда вам придется использовать дополнительный файловый дескриптор.

большинство команд имеют один входной канал (стандартный ввод, дескриптор файла 0) и один выходной канал (стандартный вывод, дескриптор файла 1) или работают с несколькими файлами, которые они открывают сами (поэтому вы передаете им имя файла). (это в дополнение к стандартной ошибке (fd 2), которая обычно фильтруется до пользователя.) однако иногда удобно иметь команду, которая действует как фильтр от нескольких источников или нескольких целей. например, вот простой скрипт, который отделяет нечетные строки в файле от четныхwhile ifs= read -r line; do printf '%sn' "$line" if ifs= read -r line; then printf '%s\n' "$line" >&3; fi done >odd.txt 3>even.txt теперь предположим, что вы хотите применить другой фильтр к строкам с нечетными номерами и к строкам с четными номерами (но не собирать их вместе, это было бы другой проблемой, неосуществимой в командной оболочке в целом). в командной консоли вы можете передавать стандартный вывод команды только другой команде; чтобы передать другой дескриптор файла, вам нужно сначала перенаправить его на fd 1.{ while … done | odd-filter >filtered-odd.txt; } 3>&1 | even-filter >filtered-even.txt другой, более простой вариант использования <a href="https://unix.stackexchange.com/questions/3514/how-to-grep-standard-error-stream-stderr/3540#3540">фильтрация вывода ошибок команды .exec m>&n перенаправляет файловый дескриптор на другой для оставшейся части сценария (или до тех пор, пока другая такая команда снова не изменит файловые дескрипторы). существует некоторое совпадение в функциональности между exec m>&n а также somecommand m>&n. форма exec является более мощной в том смысле, что ее не нужно вкладывать:exec 8<&0 9>&1 exec >output12 command1 exec <input23 command2 exec >&9 command3 exec <&8 другие примеры, которые могут представлять интерес:  <a href="https://unix.stackexchange.com/questions/42728/what-does-31-12-23-do-in-a-script">что "3> & 1 1> & 2 2> & 3" делает в скрипте? (он заменяет стандартный вывод на stderr)  <a href="https://unix.stackexchange.com/questions/13724/file-descriptors-shell-scripting/13726#13726">дескрипторы файлов и сценарии оболочки   <a href="https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer/11954#11954">насколько велика труба буфера?   <a href="https://unix.stackexchange.com/questions/4806/bash-script-testing-if-a-command-has-run-correctly/4808#4808">тестирование bash-скрипта, если команда была выполнена правильно  и еще больше примеров:  <a href="https://unix.stackexchange.com/questions/tagged/io-redirection">вопросы с тегом io-redirection   <a href="https://unix.stackexchange.com/questions/tagged/file-descriptors">вопросы с тегом file-descriptors   <a href="http://data.stackexchange.com/unix%20and%20linux/s/1740/examples-of-shell-redirections-involving-file-descriptors-3-and-above">поиск примеров на этом сайте в <a href="http://data.stackexchange.com/">data explorer (общедоступная копия базы данных stack exchange только для чтения) постскриптум это удивительный вопрос от автора <a href="https://unix.stackexchange.com/questions/6430/how-to-redirect-stderr-out-to-different-files-and-also-display-in-terminal/6431#6431">наиболее одобренного сообщения на сайте, использующего перенаправление через fd !

вот пример использования дополнительных fd в качестве контроля чата bash-скрипта:#!/bin/bash log() { echo $* >&3 } info() { echo $ >&4 } err() { echo $* >&2 } debug() { echo $ >&5 } verbose=1 while [[ $# -gt 0 ]]; do arg=$1 shift case $arg in "-vv") verbose=3 ;; "-v") verbose=2 ;; "-q") verbose=0 ;; # more flags *) echo -n # linear args ;; esac done for i in 1 2 3; do fd=$(expr 2 + $i) if [[ $verbose -ge $i ]]; then eval "exec $fd>&1" else eval "exec $fd> /dev/null" fi done err "this will \_always\_ show up." log "this is normally displayed, but can be prevented with -q" info "this will only show up if -v is passed" debug "this will show up for -vv" </code></pre>

<p>в контексте именованных каналов (fifos) использование дополнительного файлового дескриптора может включить неблокирующее поведение конвейера.</p><pre><code>( rm -f fifo mkfifo fifo exec 3<fifo # open fifo for reading trap "exit" 1 2 3 15 exec cat fifo | nl ) & bpid=$! ( exec 3>fifo # open fifo for writing trap "exit" 1 2 3 15 while true; do echo "blah" > fifo done ) #kill -term $bpid </code></pre><p>смотрите: <a href="https://stackoverflow.com/questions/6834347/named-pipe-closing-prematurely-in-script/">named pipe преждевременно закрывается в скрипте?</a> </p>

<p>дополнительный файловый дескриптор подходит для случаев, когда вы хотите перехватить стандартный вывод в переменной, но все же хотите вывести его на экран, например, в пользовательском интерфейсе bash-скрипта.</p><pre><code>arg1 string to echo arg2 flag 0,1 print or not print to 3rd fd stdout descriptor function ecko3 { if [ "$2" -eq 1 ]; then exec 3>$(tty) echo -en "$1" | tee >(cat - >&3) exec 3>&- else echo -en "$1" fi } </code></pre>

<p>вот еще один сценарий, когда использование дополнительного файлового дескриптора кажется подходящим (в bash):</p><p> <a href="https://stackoverflow.com/questions/6607675/shell-script-password-security-of-command-line-parameters">защита паролем сценария командной строки параметров командной строки</a> </p><pre><code>env -i bash --norc # clean up environment set +o history read -s -p "enter your password: " passwd exec 3<<<"$passwd" mycommand <&3 # cat /dev/stdin in mycommand 

пример: использование flock для принудительного запуска сценариев с блокировками файловодним из примеров является использование блокировки файлов для принудительного запуска сценариев по всей системе. это полезно, если вы не хотите, чтобы два скрипта одного типа работали с одинаковыми файлами. в противном случае два сценария будут мешать друг другу и, возможно, повреждать данные.#exit if any command returns a non-zero exit code (like flock when it fails to lock) set -e #open file descriptor 3 for writing exec 3> /tmp/file.lock #create an exclusive lock on the file using file descriptor 3 #exit if lock could not be obtained flock -n 3 #execute serial code #remove the file while the lock is still obtained rm -f /tmp/file.lock #close the open file handle which releases the file lock and disk space exec 3>&- функционально использовать стадо, определив блокировку и разблокировкувы также можете обернуть эту логику блокировки/разблокировки в многократно используемые функции. следующее <a href="https://www.gnu.org/software/bash/manual/html_node/bourne-shell-builtins.html#index-trap">trap shell buildin автоматически снимет блокировку файла при выходе из скрипта (либо ошибка, либо успех). trap помогает очистить блокировки файлов. тропинка /tmp/file.lock должен быть жестко закодированным путем, чтобы несколько скриптов могли попытаться заблокировать его.# obtain a file lock and automatically unlock it when the script exits function lock() { exec 3> /tmp/file.lock flock -n 3 && trap unlock exit } # release the file lock so another program can obtain the lock function unlock() { # only delete if the file descriptor 3 is open if { >&3 ; } &> /dev/null; then rm -f /tmp/file.lock fi #close the file handle which releases the file lock exec 3>&- } логика unlock, приведенная выше, предназначена для удаления файла до снятия блокировки. таким образом, он очищает файл блокировки. поскольку файл был удален, другой экземпляр этой программы может получить блокировку файла.использование функций блокировки и разблокировки в скриптахвы можете использовать его в своих скриптах, как в следующем примере.#exit if any command returns a non-zero exit code (like flock when it fails to lock) set -e #try to lock (else exit because of non-zero exit code) lock #system-wide serial locked code unlock #non-serial code если вы хотите, чтобы ваш код ожидал блокировки, вы можете настроить скрипт следующим образом:set -e #wait for lock to be successfully obtained while ! lock 2> /dev/null; do sleep .1 done #system-wide serial locked code unlock #non-serial code 

в качестве конкретного примера, я только что написал скрипт, которому нужна информация о синхронизации из подкоманды. использование дополнительного файлового дескриптора позволило мне перехватить stderr команды time без прерывания stdout или stderr подкоманды.(time ls -9 2>&3) 3>&2 2> time.txt это указывает на то, что stderr ls указывает на fd 3, указывает fd 3 на stderr скрипта и указывает stderr time на файл. когда скрипт запускается, его stdout и stderr совпадают с подкомандой, которая может быть перенаправлена как обычно. только вывод time перенаправляется в файл.$ echo '(time ls my-example-script.sh missing-file 2>&3) 3>&2 2> time.txt' > my-example-script.sh $ chmod +x my-example-script.sh $ ./my-example-script.sh ls: missing-file: no such file or directory my-example-script.sh $ ./my-example-script.sh > /dev/null ls: missing-file: no such file or directory $ ./my-example-script.sh 2> /dev/null my-example-script.sh $ cat time.txt real 0m0.002s user 0m0.001s sys 0m0.001s </code></pre>