mdfind使用Python子进程的不同结果的原因

时间:2014-11-19 12:32:54

标签: python unix subprocess

我正在尝试为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命令所执行的一项?

1 个答案:

答案 0 :(得分:1)

我确信这与命令行参数处理管道中的细微但重要的差异有关。这个管道很复杂,当从编程语言环境中调用命令时,实际上很难获得等效的行为,因为在你最喜欢的shell中键入命令。

坏处是:取决于目标可执行文件用于解析命令行参数的方法(遗憾的是 - 在许多情况下 - 没有明确的标准),结果可能因调用方法而异。也就是说,你的观察肯定与空格,NULL字符,破折号和引号的处理和解释有关。

你的问题是“为什么?”。因此,如果您真的想深究这一点,您需要查看Python子进程模块的源代码以及目标命令命令行参数解析代码的源代码。此外,您可能希望阅读这些内容:

为了获得概念上等效的行为,就像在shell中输入一样,有一个非显而易见但又简单的解决方法:创建一个临时shell脚本并从Python中调用shell并仅提供一个参数:shell脚本的路径。我在这个模块中使用了这个方法来创建系统的命令行工具测试: https://github.com/jgehrcke/timegaps/blob/master/test/clitest.py