在 Python 中,自定义文件描述符如何用于输入和输出,包括默认设置和最终关闭?

In Python, how are custom file descriptors used for input and output, including defaults setup and final closing?

我想了解自定义文件描述符如何在 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,  is the return code returned when calling main
main ""

调用

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

return代码

$ 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 和自定义文件描述符,我们将不胜感激。

Python 2的file object is a fairly thin wrapper over C's stdio FILE structure,它本身包含相应的描述符(一个整数)。在很多地方,文档都引用了 underlying/related stdio 的东西,这并非巧合。

  • 每次创建文件对象 (open()) 时,都会打开一个对应于该文件的描述符,并在对该对象的所有 I/O 操作中使用。

    • 你可以用<file>.fileno()获得它。
    • 相反,如果你有一个原始描述符,你可以用 os.fdopen() 的文件对象包装它。
      • 例如如果您命令 bash 重定向脚本的特定描述符,它会为您的子进程打开相应的描述符。
  • 当文件对象被关闭或垃圾回收时,底层描述符也被关闭。

    • 由于垃圾收集时间未知,从 2.6 开始,建议使用 with open(...) as f: 在块的末尾强制关闭,这样文件就不会打开超过必要的时间。
    • .
  • os module 还有一些其他函数可以与描述符一起使用,这些描述符反映了相应的 C 函数,例如 os.dup().

通常,您应该使用文件对象,不要理会它们的底层描述符。即使使用 return 原始描述符 like with os.pipe().


示例:

(尖括号中的实体是伪代码,表示要插入的内容)

检查描述符是否存在:

同时 How to check if a given file descriptor stored in a variable is still valid? suggests (UNIX-only) fcntl or (portable) dup as the least intrusive ways, since you're going to use it via a file object, it's best to just attempt to:

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'

重复的 FD

不是真的需要 - 你可以分配例如f = sys.stdin 根据条件使用 f您真正需要它的唯一情况是您必须向其他进程提供额外的 FD。

例如复制文件对象的 FD 并在副本上创建另一个文件对象:

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

阅读 from/Writing to/closing 一个 FD

读取 from/write to/close/whatever 包装该 FD 的文件对象。请参阅 Reading and Writing files - Python tutorial,唯一的区别是如果您有原始 FD,则使用 os.fdopen() 而不是 open() 创建文件对象。