В Python как пользовательские файловые дескрипторы используются для ввода и вывода, включая настройку по умолчанию и окончательное закрытие?

Я хочу понять, как пользовательские файловые дескрипторы работают в Python для ввода, вывода, настройки по умолчанию и окончательного закрытия. У меня есть файл в Bash, который делает именно то, что я хочу сделать в Python. Может кто-нибудь сказать мне, как это будет сделано в Python? Я использую Python 2.7.5, Bash 4.2 и выполняю на CentOS 7.3.

настроить

$ echo "input string" > input

bash_fd.sh

#!/bin/bash

# Demonstration of custom file descriptors in Bash
#     3  script input (scrin)
#     4  script output (scrout)
#     5  script error (screrr)
#     6  script data (scrdata, demo: JSON return payload)
#     7  script log (scrlog)

fd_open()
{
   ### Provide defaults for file descriptors 3-7 ONLY if the FDs are undefined
   { >&3; } 2>/dev/null || exec 3<&0            # dup scrin   to stdin
   { >&4; } 2>/dev/null || exec 4>&1            # dup scrout  to stdout
   { >&5; } 2>/dev/null || exec 5>&2            # dup screrr  to stderr
   { >&6; } 2>/dev/null || exec 6>/dev/null     # set scrdata to /dev/null
   { >&7; } 2>/dev/null || exec 7>/dev/null     # set scrlog  to /dev/null
}

fd_close()
{
   # Close all file descriptors
   exec 3>&-
   exec 4>&-
   exec 5>&-
   exec 6>&-
   exec 7>&-
}

main()
{
   fd_open                                      # Ensure 

   echo "[$(date)] Program beginning" >&7       # scrlog

   echo -n 'Enter a message: ' >&4              # scrout
   read MSG <&3                                 # scrin

   echo "Read message $MSG" >&4                 # scrout
   echo "[screrr] Read message $MSG" >&5        # screrr

   echo "{\"msg\": \"$MSG\"}" >&6               # scrdata: return JSON payload
   echo "[$(date)] Program finishing: $MSG" >&7 # scrlog

   fd_close

   return ${1:-0}                               # return status code
}

# For demonstration purposes, $1 is the return code returned when calling main
main "$1"

вызов

$ ./bash_fd.sh 37 3<input 4>scrout 5>screrr 6>scrdata 7>scrlog
$

код возврата

$ echo $?
37

сгенерированные файлы

$ cat scrout
Enter a message: Read message input string

$ cat screrr
[screrr] Read message input string

$ cat scrdata
{"msg": "input string"}

$ cat scrlog
[Wed Jun 14 21:33:24 EDT 2017] Program beginning
[Wed Jun 14 21:33:24 EDT 2017] Program finishing: input string

Любая помощь в переводе вышеупомянутого скрипта Bash на Python действительно поможет мне понять Python и пользовательские файловые дескрипторы и будет высоко оценена.

1 ответ

Решение

Файловый объект Python 2 является довольно тонкой оболочкой над C stdioFILE структура, которая сама содержит соответствующий дескриптор (целое число). Это не совпадение, что во многих местах документы ссылаются на stdio вещи.

  • Каждый раз, когда вы создаете файл объекта (open()), дескриптор, соответствующий файлу, открывается и используется во всех операциях ввода-вывода с объектом.

    • Вы можете получить это с <file>.fileno(),
    • и наоборот, если у вас есть необработанный дескриптор, вы можете обернуть его файловым объектом с помощью os.fdopen(),
      • например, если вы приказали bash чтобы перенаправить определенные дескрипторы вашего скрипта, он открыл соответствующие дескрипторы для вашего подпроцесса.
  • Когда файловый объект закрывается или собирается мусором, базовый дескриптор также закрывается.

  • os Модуль имеет несколько других функций для работы с дескрипторами, которые отражают соответствующие функции C, такие как os.dup(),

Как правило, вы должны использовать файловые объекты и не беспокоиться об их базовых дескрипторах. Вы можете сделать это даже с функциями, которые возвращают необработанные дескрипторы, как с os.pipe(),


Примеры:

(объекты в угловых скобках - это псевдокод, показывающий, что нужно вставить туда)

Проверьте, существует ли дескриптор:

Как проверить, является ли данный файловый дескриптор, сохраненный в переменной, действительным? предлагает (только для UNIX) fcntl или (переносной) dup в качестве наименее навязчивых способов, так как вы собираетесь использовать его через файловый объект, лучше всего попытаться:

import os,errno    
<...>
try: f = os.fdopen(<fd>)
except OSError as e:
    if e.errno!=errno.EBADF: raise
    else:
        # actions when doesn't exist, maybe create `f' some other way
else:
    #actions when exists
# use `f'

Дубликаты FDs

Не очень нужно - вы можете просто назначить, например, f = sys.stdin в зависимости от состояния и использования f, Единственный случай, когда вам действительно это нужно, это если вы должны предоставить дополнительные FD другим процессам.

  • new_fd = os.dup(fd) использует наименьшее доступное число для нового FD
  • os.dup2(old_fd,new_fd) использует определенный, закрывая его, если он уже открыт

Например, чтобы дублировать FD файлового объекта и создать еще один файловый объект поверх дубликата:

os.dup2(old_f.fileno(),<new_fd>)
new_f = os.fdopen(<new_fd>)

Чтение из / Запись в / закрытие FD

Чтение из / запись в / закрытие / любой объект файла, обертывающий этот FD. См. Чтение и запись файлов - учебник по Python, единственное отличие состоит в том, что если у вас есть сырой FD, вы создаете объект файла с os.fdopen() вместо open(),

Другие вопросы по тегам