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

标签 python unix subprocess

我正在尝试为 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的子进程模块的源代码和目标命令命令行参数解析代码的源代码。另外,您可能希望阅读以下内容:

为了获得与在 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/

相关文章:

python - 无法在 AWS Lambda (Python) 中导入模块

python - 在 Maya 2017 中使用 PyMel 获取矩阵的逆

linux - Matlab - 在不打开 GUI 的情况下运行文件,然后退出

linux - 为 unix-layman 在 unix 中使用解释器运行 r 脚本或命令

python错误代码处理

python - 高效生成斯特恩双原子序列

python - Datadog 计数指标因多个容器而下降

c - 当 realloc 缩小分配的 block 时,释放的内存在哪里?

python - 使用 Subprocess、Python 和 PSQL 进行多处理

python - 子进程check_output,Popen,getoutput python之间的区别