/usr/bin/which 作为 python 子进程调用时有奇怪的行为

/usr/bin/which has strange behavior when called as python subprocess

我有一个 python (2.7) 包,它依赖于用 C 编写的库(大脑成像套件 Freesurfer)。为了检查库,我写了以下内容:

def crash_if_freesurfer_not_found():
  import os, subprocess
  with open(os.devnull) as devnull:
    p = subprocess.call(['which', 'mri_info'], stdout=devnull, stderr=devnull)
  if p!=0:
    print 'Useful error message'
    sys.exit(1)

如果 which 在路径中找到程序 mri_info,那么它有 return 代码 0,否则它有 return 代码 1。在我开发这个的系统上,这个成功了。

我现在在一个系统上,这段代码失败了。但我很困惑为什么,因为 `os.environ['PATH'] 包括 ~/freesurfer/bin,这个程序所在的位置。

In [3]: os.environ['PATH'].split(':')[0]
Out[3]: '/home/aestrivex/freesurfer/freesurfer/bin'

In [11]: ls /home/aestrivex/freesurfer/freesurfer/bin | grep mri_info
  mri_info*

所以我深入挖掘,发现了这种我不理解的奇怪行为:

In [10]: with open(os.devnull) as nil:
  p = subprocess.call(['which', 'mri_info'], stdout=nil, stderr=nil)
  ....:     

In [11]: p
Out[11]: 1

In [12]: with open(os.devnull) as nil:
  p = subprocess.call(['which', 'mri_info'], stdout=nil)
  ....:     
  sh: printf: I/O error

In [13]: with open(os.devnull) as nil:
  p = subprocess.call(['which', 'mri_info'])
  ....:     
  /home/aestrivex/freesurfer/freesurfer/bin/mri_info

aestrivex@apocrypha ~/gselu $ which which
  /usr/bin/which

因此,只要 python 子进程将标准输出重定向到 /dev/null,which 就会失败并出现 I/O 错误,否则会正常运行。

但是我从 which 得到完全正常的行为,而不是在 python 子进程

aestrivex@apocrypha ~/gselu $ which mri_info
/home/aestrivex/freesurfer/freesurfer/bin/mri_info
aestrivex@apocrypha ~/gselu $ which mri_info > /dev/null
aestrivex@apocrypha ~/gselu $ echo $?
0

我有几种方法可以修复此检查,但我的问题是,什么可能的上下文会导致此错误在 python 子进程中看到 which 表现得像这样?

您正在以默认模式(即 'r' [阅读])打开 "file" (/dev/null)。但是,subprocess 正在尝试 写入文件——因此您可能想要:

with open(os.devnull, 'w') as nil:
  p = subprocess.call(['which', 'program'], stdout=nil, stderr=nil)