将字符串传递给 subprocess.run 似乎将其用单引号括起来否定了通配符搜索?
Passing string to subprocess.run seems to encase it in single quotes negating a wildcard search?
我的机器上有一组与 PCI 设备 (GPU) 相关的目录。在这些目录中有各种 hwmon
接口。我试图通过 运行 命令 find
每个 PCI 设备的特定路径,例如
$ find /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:00.0/0000:03:00.0/hwmon/hwmon* -maxdepth 0
此处通配符将匹配每个 PCI 设备位于 .../hwmon/
下的单个目录。上面的命令在我的终端中输出以下内容
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:00.0/0000:03:00.0/hwmon/hwmon2
我正尝试在 python 中使用 subprocess.run
自动执行此过程
subprocess.run('find', gpu_pci_device_path + '/hwmon/hwmon*', '-maxdepth', '0', stdout=subprocess.PIPE).stdout.decode('utf-8')
在这里,我已经在变量 gpu_pci_device_path
中找到(我称之为)每个 GPU 的 PCI 设备路径。然后我构建通配符路径的其余部分以传递给 find
.
尽管看起来 subprocess
将我根据它产生的错误用单引号构建的路径括起来
find: ‘/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:00.0/0000:03:00.0/hwmon/hwmon*’: No such file or directory
因此它否定了我在 find
期间的通配符表达式。 如何在调用 subprocess.run
期间将此通配符表达式提供给 find
?
我证明正在发生封装并且通配符被否定的证据来自命令的输出
$ find '/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:00.0/0000:03:00.0/hwmon/hwmon*' -maxdepth 0
find: ‘/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:00.0/0000:03:00.0/hwmon/hwmon*’: No such file or directory
简单(Shell-免费)答案
使用 shell=True
是不好的做法,您没有理由在这里这样做:Python 可以使用标准库功能自行执行 globbing。
subprocess.run((['find'] +
glob.glob(gpu_pci_device_path + '/hwmon/hwmon*') +
['-maxdepth', '0']),
stdout=subprocess.PIPE).stdout.decode('utf-8')
这与真实的 shell 行为 相当 不同:如果没有路径匹配,这只会使 find
的第一个参数成为 -maxdepth
,shell 只有在设置了非默认 nullglob
选项的情况下才会这样做。要解决此问题,请参阅下文。
较长(Shell-兼容)答案
为了更接近地模拟 shell 的行为,我们可能会在 glob.glob()
周围添加一个包装器,使默认语义更 POSIX-y,并支持 bash 的 failglob
和 nullglob
选项(在 bash 中可以使用 shopt
启用)以在找不到匹配文件时调整行为,并且 globstar
使 **
可以选择性地递归:
import errno
import glob
import os
import subprocess
def glob_or_literal(expression, nullglob=False, failglob=False, globstar=False):
literal_result = glob.glob(expression, recursive=globstar)
if literal_result or nullglob:
return literal_result
if failglob:
raise FileNotFoundError(errno.ENOENT, os.strerr(errno.ENOENT), expression)
return [expression]
subprocess.run((['find'] +
glob_or_literal(gpu_pci_device_path + '/hwmon/hwmon*') +
['-maxdepth', '0']),
stdout=subprocess.PIPE).stdout.decode('utf-8')
...在这种情况下,如果不存在 hwmon
目录,您将获得传递给 find
的文字 glob 表达式,让它写出适当有用的错误消息(就像 POSIX-family shells do by default), unless you set the nullglob
option (instructing the function, or a shell, to make a non-matching glob not expand to任何参数——可能有不需要的行为,比如 GNU find
决定在当前目录中开始搜索),或者 failglob
选项(这使得 shell,或者在这种情况下glob_or_literal
Python 函数,在开始 find
之前中止)。
我的机器上有一组与 PCI 设备 (GPU) 相关的目录。在这些目录中有各种 hwmon
接口。我试图通过 运行 命令 find
每个 PCI 设备的特定路径,例如
$ find /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:00.0/0000:03:00.0/hwmon/hwmon* -maxdepth 0
此处通配符将匹配每个 PCI 设备位于 .../hwmon/
下的单个目录。上面的命令在我的终端中输出以下内容
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:00.0/0000:03:00.0/hwmon/hwmon2
我正尝试在 python 中使用 subprocess.run
subprocess.run('find', gpu_pci_device_path + '/hwmon/hwmon*', '-maxdepth', '0', stdout=subprocess.PIPE).stdout.decode('utf-8')
在这里,我已经在变量 gpu_pci_device_path
中找到(我称之为)每个 GPU 的 PCI 设备路径。然后我构建通配符路径的其余部分以传递给 find
.
尽管看起来 subprocess
将我根据它产生的错误用单引号构建的路径括起来
find: ‘/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:00.0/0000:03:00.0/hwmon/hwmon*’: No such file or directory
因此它否定了我在 find
期间的通配符表达式。 如何在调用 subprocess.run
期间将此通配符表达式提供给 find
?
我证明正在发生封装并且通配符被否定的证据来自命令的输出
$ find '/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:00.0/0000:03:00.0/hwmon/hwmon*' -maxdepth 0
find: ‘/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:00.0/0000:03:00.0/hwmon/hwmon*’: No such file or directory
简单(Shell-免费)答案
使用 shell=True
是不好的做法,您没有理由在这里这样做:Python 可以使用标准库功能自行执行 globbing。
subprocess.run((['find'] +
glob.glob(gpu_pci_device_path + '/hwmon/hwmon*') +
['-maxdepth', '0']),
stdout=subprocess.PIPE).stdout.decode('utf-8')
这与真实的 shell 行为 相当 不同:如果没有路径匹配,这只会使 find
的第一个参数成为 -maxdepth
,shell 只有在设置了非默认 nullglob
选项的情况下才会这样做。要解决此问题,请参阅下文。
较长(Shell-兼容)答案
为了更接近地模拟 shell 的行为,我们可能会在 glob.glob()
周围添加一个包装器,使默认语义更 POSIX-y,并支持 bash 的 failglob
和 nullglob
选项(在 bash 中可以使用 shopt
启用)以在找不到匹配文件时调整行为,并且 globstar
使 **
可以选择性地递归:
import errno
import glob
import os
import subprocess
def glob_or_literal(expression, nullglob=False, failglob=False, globstar=False):
literal_result = glob.glob(expression, recursive=globstar)
if literal_result or nullglob:
return literal_result
if failglob:
raise FileNotFoundError(errno.ENOENT, os.strerr(errno.ENOENT), expression)
return [expression]
subprocess.run((['find'] +
glob_or_literal(gpu_pci_device_path + '/hwmon/hwmon*') +
['-maxdepth', '0']),
stdout=subprocess.PIPE).stdout.decode('utf-8')
...在这种情况下,如果不存在 hwmon
目录,您将获得传递给 find
的文字 glob 表达式,让它写出适当有用的错误消息(就像 POSIX-family shells do by default), unless you set the nullglob
option (instructing the function, or a shell, to make a non-matching glob not expand to任何参数——可能有不需要的行为,比如 GNU find
决定在当前目录中开始搜索),或者 failglob
选项(这使得 shell,或者在这种情况下glob_or_literal
Python 函数,在开始 find
之前中止)。