Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/294.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python、子流程、call()、检查_call和returncode以查找命令是否存在_Python_Command Line_Call_Subprocess_File Exists - Fatal编程技术网

Python、子流程、call()、检查_call和returncode以查找命令是否存在

Python、子流程、call()、检查_call和returncode以查找命令是否存在,python,command-line,call,subprocess,file-exists,Python,Command Line,Call,Subprocess,File Exists,我已经了解了如何使用call()让python脚本运行命令: import subprocess mycommandline = ['lumberjack', '-sleep all night', '-work all day'] subprocess.call(mycommandline) 这是可行的,但有一个问题,如果用户的命令路径中没有lumberjack怎么办?如果lumberjack与python脚本放在同一个目录中,它会工作,但是脚本如何知道它应该查找lumberjack?我想

我已经了解了如何使用call()让python脚本运行命令:

import subprocess

mycommandline = ['lumberjack', '-sleep all night', '-work all day']
subprocess.call(mycommandline)
这是可行的,但有一个问题,如果用户的命令路径中没有lumberjack怎么办?如果lumberjack与python脚本放在同一个目录中,它会工作,但是脚本如何知道它应该查找lumberjack?我想如果有一个命令未找到的错误,那么lumberjack就不会在命令路径中,脚本可以尝试找出它的目录是什么,并在那里查找lumberjack,最后警告用户,如果在这两个位置中都找不到它,就将lumberjack复制到这两个位置中的一个。如何找出错误消息是什么?我了解到check_call()可以返回错误消息和有关returncode属性的信息。我找不到关于如何使用check_call()和returncode、消息是什么或者如何判断消息是否未找到的示例


我这样做是否正确?

子流程
将在未找到命令时引发异常,
OSError

当找到该命令,并且
子流程
为您运行该命令时,将从该命令返回结果代码。标准是代码0表示成功,而任何失败都是一些非零错误代码(这会有所不同;请查看文档以了解您正在运行的特定命令)

因此,如果捕获
OSError
,您可以处理不存在的命令,如果检查结果代码,您可以确定命令是否成功

子流程
的好处在于,您可以让它收集
stdout
stderr
中的所有文本,然后您可以丢弃它或返回它,或记录它或显示它。我经常使用一个包装器来丢弃命令的所有输出,除非命令失败,在这种情况下,
stderr
中的文本被输出

我同意你不应该要求用户复制可执行文件。程序应位于
PATH
变量中列出的目录中;如果程序丢失,则应将其安装,或者如果程序安装在不在
路径
上的目录中,则用户应更新
路径
以包含该目录

请注意,您可以选择使用各种硬编码的可执行文件路径多次尝试
子流程

import os
import subprocess as sp

def _run_cmd(s_cmd, tup_args):
    lst_cmd = [s_cmd]
    lst_cmd.extend(tup_args)
    result = sp.call(lst_cmd)
    return result

def run_lumberjack(*tup_args):
    try:
        # try to run from /usr/local/bin
        return _run_cmd("/usr/local/bin/lumberjack", tup_args)
    except OSError:
        pass

    try:
        # try to run from /opt/forest/bin
        return _run_cmd("/opt/forest/bin/lumberjack", tup_args)
    except OSError:
        pass

    try:
        # try to run from "bin" directory in user's home directory
        home = os.getenv("HOME", ".")
        s_cmd = home + "/bin/lumberjack"
        return _run_cmd(s_cmd, tup_args)
    except OSError:
        pass

    # Python 3.x syntax for raising an exception
    # for Python 2.x, use:  raise OSError, "could not find lumberjack in the standard places"
    raise OSError("could not find lumberjack in the standard places")

run_lumberjack("-j")
编辑:经过一点思考,我决定完全重写上面的内容。只需传递一个位置列表,然后循环尝试其他位置,直到其中一个位置起作用,这会更干净。但是如果不需要的话,我不想为用户的主目录构建字符串,所以我只是将一个可调用的字符串放入替代列表中是合法的。如果你对此有任何问题,尽管问

import os
import subprocess as sp

def try_alternatives(cmd, locations, args):
    """
    Try to run a command that might be in any one of multiple locations.

    Takes a single string argument for the command to run, a sequence
    of locations, and a sequence of arguments to the command.  Tries
    to run the command in each location, in order, until the command
    is found (does not raise OSError on the attempt).
    """
    # build a list to pass to subprocess
    lst_cmd = [None]  # dummy arg to reserve position 0 in the list
    lst_cmd.extend(args)  # arguments come after position 0

    for path in locations:
        # It's legal to put a callable in the list of locations.
        # When this happens, we should call it and use its return
        # value for the path.  It should always return a string.
        if callable(path):
            path = path()

        # put full pathname of cmd into position 0 of list    
        lst_cmd[0] = os.path.join(path, cmd)
        try:
            return sp.call(lst_cmd)
        except OSError:
            pass
    raise OSError('command "{}" not found in locations list'.format(cmd))

def _home_bin():
    home = os.getenv("HOME", ".")
    return os.path.join(home, "bin")

def run_lumberjack(*args):
    locations = [
        "/usr/local/bin",
        "/opt/forest/bin",
        _home_bin, # specify callable that returns user's home directory
    ]
    return try_alternatives("lumberjack", locations, args)

run_lumberjack("-j")
一个简单的片段:

try:
    subprocess.check_call(['executable'])
except subprocess.CalledProcessError:
    pass # handle errors in the called executable
except OSError:
    pass # executable not found

哇,太快了!我结合了Theodros Zelleke的简单示例和steveha对函数的使用,以及abarnert对OSError的评论和Lattyware对移动文件的评论:

import os, sys, subprocess

def nameandpath():
    try:
        subprocess.call([os.getcwd() + '/lumberjack']) 
        # change the word lumberjack on the line above to get an error
    except OSError:
        print('\nCould not find lumberjack, please reinstall.\n')
        # if you're using python 2.x, change the () to spaces on the line above

try:
    subprocess.call(['lumberjack'])
    # change the word lumberjack on the line above to get an error
except OSError:
    nameandpath()
我在MacOS-X(6.8/SnowLeopard)、Debian(Squeeze)和Windows(7)上测试了它。在所有三个操作系统上,它似乎都按照我希望的方式工作。我尝试使用check_call和CalledProcessError,但无论我做了什么,我似乎每次都会出错,而且我无法让脚本处理错误。为了测试脚本,我将名称从'lumberjack'更改为'deadparrot',因为我的脚本目录中有lumberjack


您认为这个脚本的编写方式有任何问题吗?

check_call()
如果命令没有干净地退出(这是不存在的命令)应该会引发错误。此外,我建议不要告诉用户复制可执行文件,除非作为最后手段。软件包管理器几乎总是安装东西的最佳选择。@DaveBrunker:你似乎对
call
check\u call
有点困惑
call
已经将返回代码作为函数的返回值,而不是属性。使用
check\u call
的原因是它负责为您检查返回代码,并引发
CalledProcessError
。任何一个都会为找不到的程序引发一个
OSError
(或类似的,取决于您的Python版本)。@inspectorG4dget:实际上,不存在的命令都不会以不干净的方式退出,因为它们甚至无法启动。这就是为什么他们提出
OSError
,而不是
CalledProcessError
,即使是
call
,也会这样做,而不仅仅是
check\u call
。见西奥德罗斯·泽莱克的回答。