我正在尝试为 UNIX 编写一个 Python 包装器 mdfind
公用事业。以最简单的形式,它效果很好;然而,我无法找出任何奇怪行为的例子。当运行更复杂的查询(两个或更多字段)时,事情会变得有点奇怪。举个例子:
import subprocess
import itertools
def test1():
cmd = "mdfind 'kMDItemFSName=pandoc&&kMDItemContentType=public.unix-executable'"
shell_res = subprocess.check_output(cmd, shell=True)
find_res = mdfind(content_type='public.unix-executable',
name='pandoc')
if shell_res == find_res:
print('Passed!')
def mdfind(**kwargs):
cmd = ['mdfind']
for key, arg in kwargs.iteritems():
if key in mdattributes().keys():
md_name = mdattributes()[key]['id']
query = '='.join([md_name, arg])
cmd.append(query)
if 'only_in' in kwargs:
cmd.append('-onlyin')
cmd.append(kwargs['only_in'])
return subprocess.check_output(cmd)
def mdattributes():
attributes_str = subprocess.check_output(['mdimport', '-A'])
# prepare key names for the four columns
keys = ('id', 'name', 'description', 'aliases')
# create list of dicts, mapping ``keys`` to an item's columns
data = [dict(itertools.izip(keys,
[item.replace("'", "")
for item in attribute.split('\t\t')]))
for attribute in attributes_str.splitlines()]
# coerce list of dicts into large dict with nested dicts
metadata = {}
for md_dict in data:
# clean up key
key = md_dict['id'].replace('kMDItemFS', '')\
.replace('kMDItem', '')\
.replace('kMD', '')\
.replace('com_', '')
metadata[key] = md_dict
return metadata
test1()
此代码将通过,因为直接 shell 命令和包装器创建的命令将输出相同的结果。
现在,以这个例子为例,在我看来,它是同一类,但不起作用:
def test2():
cmd = """mdfind 'kMDItemKind=PDF&&kMDItemFSName="*epistem*"c'"""
shell_res = run_shell(cmd)
find_res = mdfind(kind='PDF',
name='"*epistem*"c')
直接 shell 命令将返回我的机器上标题中包含“Epistemology”的单个 PDF,而包装命令将返回 13 个 PDF(我的机器上总共有 1,000 多个 PDF)。因此,包装器脚本以某种方式过滤了数千个 PDF,但显然不是通过是否 *epistem*
来过滤的。是在标题中。
更奇怪的是,这个命令将返回 144 个结果:
subprocess.check_output(['mdfind',
"""kMDItemKind=PDF&&kMDItemFSName="*epistemolog*"c"""])
因此,简而言之,这三个不同的子进程调用给出的结果数量完全不同:
"""mdfind 'kMDItemKind=PDF&&kMDItemFSName="*epistem*"c'"""
['mdfind', 'kMDItemKind=PDF', u'kMDItemFSName="*epistem*"c']
['mdfind', """kMDItemKind=PDF&&kMDItemFSName="*epistemolog*"c"""]
所以,我的问题是:为什么?为什么 subprocess.check_output()
对于直接 shell 命令返回 1 个结果(我的意思是命令是一个字符串并且设置了 shell=True
),对于 3 项列表命令返回 13 个结果,对于 2 项列表命令返回 144 个结果?幕后到底发生了什么?如何让 3 项列表仅返回直接 shell 命令执行的一项?
最佳答案
我确信这与命令行参数处理管道中微妙但重要的差异有关。该管道很复杂,当从编程语言环境中调用命令时,实际上很难获得与在您最喜欢的 shell 中键入命令相同的行为。
坏事是:根据目标可执行文件使用哪种方法来解析命令行参数(不幸的是,在许多情况下,没有明确的标准),结果可能会因调用方法而异。也就是说,您的观察肯定与空白、NULL 字符、破折号和引号的处理和解释有关。
你的问题是“为什么?”。因此,如果你真的想弄清楚这一点,你需要查看Python的子进程模块的源代码和目标命令命令行参数解析代码的源代码。另外,您可能希望阅读以下内容:
- http://www.daviddeley.com/autohotkey/parameters/parameters.htm
- http://gehrcke.de/2014/02/command-line-argument-binary-data/
为了获得与在 shell 中键入内容在概念上等效的行为,有一个非显而易见但非常简单的解决方法:创建一个临时 shell 脚本并从 Python 中调用您的 shell,并仅提供一个参数:shell 脚本的路径。我在本模块中使用了这种方法来创建系统的命令行工具测试: https://github.com/jgehrcke/timegaps/blob/master/test/clitest.py
关于python - 使用 Python 子进程的 mdfind 结果不同的原因,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27016934/