it-swarm-ru.tech

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

Я знаю, что вы можете создать дескриптор файла и перенаправить вывод на него. например.

exec 3<> /tmp/foo # open fd 3.
echo a >&3 # write to it
exec 3>&- # close fd 3.

Но вы можете сделать то же самое без дескриптора файла:

FILE=/tmp/foo
echo a > "$FILE"

Я ищу хороший пример того, когда вам придется использовать дополнительный файловый дескриптор.

80
dogbane

Большинство команд имеют один входной канал (стандартный ввод, дескриптор файла 0) и один выходной канал (стандартный вывод, дескриптор файла 1) или работают с несколькими файлами, которые они открывают сами (поэтому вы передаете им имя файла). (Это в дополнение к стандартной ошибке (fd 2), которая обычно фильтруется до пользователя.) Однако иногда удобно иметь команду, которая действует как фильтр от нескольких источников или нескольких целей. Например, вот простой скрипт, который отделяет нечетные строки в файле от четных

while IFS= read -r line; do
  printf '%s\n' "$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

Другой, более простой вариант использования фильтрация вывода ошибок команды .

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

Другие примеры, которые могут представлять интерес:

И еще больше примеров:

Постскриптум Это удивительный вопрос от автора наиболее одобренного сообщения на сайте, использующего перенаправление через fd !

54
Gilles 'SO- stop being evil'

Вот пример использования дополнительных 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"
14
Fordi

В контексте именованных каналов (fifos) использование дополнительного файлового дескриптора может включить неблокирующее поведение конвейера.

(
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

Смотрите: Named Pipe преждевременно закрывается в скрипте?

8
chad

Дополнительный файловый дескриптор подходит для случаев, когда вы хотите перехватить стандартный вывод в переменной, но все же хотите вывести его на экран, например, в пользовательском интерфейсе bash-скрипта.

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 
}
6
Adam Michael Danischewski

Вот еще один сценарий, когда использование дополнительного файлового дескриптора кажется подходящим (в Bash):

защита паролем сценария командной строки параметров командной строки

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
3
bernard

Пример: использование 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>&-

Функционально использовать стадо, определив блокировку и разблокировку

Вы также можете обернуть эту логику блокировки/разблокировки в многократно используемые функции. Следующее 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
1
Sam Gleske

В качестве конкретного примера, я только что написал скрипт, которому нужна информация о синхронизации из подкоманды. Использование дополнительного файлового дескриптора позволило мне перехватить 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
1
Ben Blank