我正在尝试为UNIX mdfind
实用程序编写Python包装器。最简单的形式,效果很好;但是,我无法弄清楚奇怪行为的一个例子。运行更复杂的查询(两个或更多字段)时,事情变得有些奇怪。请看以下示例:
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命令将在我的机器上返回标题中具有“认识论”的单个PDF,而包装器命令将返回13个PDF(我的机器上总共有1000多个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命令所执行的一项?
答案 0 :(得分:1)
我确信这与命令行参数处理管道中的细微但重要的差异有关。这个管道很复杂,当从编程语言环境中调用命令时,实际上很难获得等效的行为,因为在你最喜欢的shell中键入命令。
坏处是:取决于目标可执行文件用于解析命令行参数的方法(遗憾的是 - 在许多情况下 - 没有明确的标准),结果可能因调用方法而异。也就是说,你的观察肯定与空格,NULL字符,破折号和引号的处理和解释有关。
你的问题是“为什么?”。因此,如果您真的想深究这一点,您需要查看Python子进程模块的源代码以及目标命令命令行参数解析代码的源代码。此外,您可能希望阅读这些内容:
为了获得概念上等效的行为,就像在shell中输入一样,有一个非显而易见但又简单的解决方法:创建一个临时shell脚本并从Python中调用shell并仅提供一个参数:shell脚本的路径。我在这个模块中使用了这个方法来创建系统的命令行工具测试: https://github.com/jgehrcke/timegaps/blob/master/test/clitest.py