26

私のコードは単純に次のとおりです。

file = 'C:\\Exe\\First Version\\filename.exe'
os.system(file)

このプログラムを実行すると、Windowsエラーが発生します。can't find the file specified.

「最初のバージョン」の途中の空白に問題があることがわかりました。問題を回避する方法をどのように見つけることができますか?

PS:変数'file'が引数として別の関数に渡されている場合はどうなりますか?

4

5 に答える 5

31

パスを引用符で囲むとうまくいきます。

file = 'C:\\Exe\\First Version\\filename.exe'
os.system('"' + file + '"')

しかし、より良い解決策は、subprocess代わりにモジュールを使用することです:

import subprocess
file = 'C:\\Exe\\First Version\\filename.exe'
subprocess.call([file])
于 2011-08-08T02:35:13.650 に答える
3

二重引用符で囲んでみてください。

file = '"C:\\Exe\\First Version\\filename.exe"'
os.system(file)
于 2011-08-08T02:21:53.650 に答える
2

os.systemパスを引用符で囲むことにより、パスにスペースが含まれるバイナリを起動することは事実です。(端末の使用に慣れている場合、これは非常に明白な解決策であるはずです。)ただし、それ自体では、この関数のより厄介な問題は解決されません...一度それを行うと、追加の問題に遭遇する可能性があります。あなたのコマンドへの引数!(ああ!)

現在のすべての推奨事項はsubprocess、この古い、眉をひそめた機能の代わりに、今すぐモジュールを使用することです。shlxこれらのサブプロセス関数のフラット文字列をリストに変換するために使用することもできます。私はそれらの方法でも問題や苦痛に遭遇しましたが、それについては取り繕いません...また、必要なのos.systemがシェル上の薄いラッパーだけである場合は、単に使用が簡単な場合があります。これは、暗黙的に出力ストリームをコンソール、同期的に動作するなど。このようなシェルでコマンドを実行する組み込み関数があり、解析、ラッピング、抽象化がまったくないことを願っています...

「フィルター」のないビルトインは存在しないため、これが私のソリューション パッチですos.system。これは、私のオープン ソース ライブラリから取得したものです。これは、Windows、Mac、および Ubuntu Linux でテストされています。100% 誰にでもできるわけではないことは承知しており、予想以上に複雑ですが、それほど悪くはありません。

この_system()ラッパーを呼び出す (実行する文字列を渡す) ときは、長いパスを引用符で囲み、引用符の有無にかかわらず必要な引数を含めます (つまり、ターミナルでコマンドを入力するのとまったく同じです!)。コマンドの最初の「トークン」で、これにより、Mac または Linux のパスの引用符とエスケープ スペースが削除されます。Windows では、指定された環境にあるものを実際に解決することにより、「短い名前」を使用します。コードのその部分は少しトリッキーです。基本的には、名前解決にバッチ メカニズムを使用し、結果を stderr 経由で送り返します。これは、そうでなければPopen()stdout の結果として得られるものを解析するためです。

必要なすべてのインポートと定義を含めたと思います。見逃したもの (ソースのスピネットのコピーと貼り付け) があれば、お知らせください。

from os import system, getcwd, chdir
from subprocess import Popen, PIPE

import platform
__plat = platform.system()
IS_WINDOWS = __plat == "Windows"
IS_LINUX   = __plat == "Linux"
IS_MACOS   = __plat == "Darwin"

__SCRUB_CMD_TMPL = "{0}{1}"
__DBL_QUOTE      = '"'
__SPACE          = ' '
__ESC_SPACE      = '\\ '
if IS_WINDOWS :        
    __BATCH_RUN_AND_RETURN_CMD = ["cmd","/K"] # simply assuming cmd is on the system path... 
    __BATCH_ONE_LINER_TMPLT    = "{0} 1>&2\n" # the newline triggers execution when piped in via stdin
    __BATCH_ESCAPE_PATH_TMPLT  = 'for %A in ("{0}") do @echo %~sA' 
    from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW
    __BATCH_ONE_LINER_STARTUPINFO = STARTUPINFO()
    __BATCH_ONE_LINER_STARTUPINFO.dwFlags |= STARTF_USESHOWWINDOW 

def _system( cmd, wrkDir=None ):
    if wrkDir is not None:
        initWrkDir = getcwd()
        print( 'cd "%s"' % (wrkDir,) )
        chdir( wrkDir  )
    cmd = __scrubSystemCmd( cmd )        
    print( cmd )
    system( cmd ) 
    print('')
    if wrkDir is not None: chdir( initWrkDir )

def __scrubSystemCmd( cmd ):
    """
    os.system is more convenient than the newer subprocess functions
    when the intention is to act as very thin wrapper over the shell. 
    There is just one MAJOR problem with it: 
    If the first character in the command is a quote (to escape a long path
    to the binary you are executing), then the limited (undesirable) parsing 
    built into the function can all fall apart.  So, this scrub function
    solves that...  
    """    
    if not cmd.startswith( __DBL_QUOTE ): return cmd
    cmdParts    = cmd[1:].split( __DBL_QUOTE )
    safeBinPath = _escapePath( cmdParts[0] )
    args        = __DBL_QUOTE.join( cmdParts[1:] ) # (the leading space will remain)
    return __SCRUB_CMD_TMPL.format( safeBinPath, args ) 
                 
def _escapePath( path ):
    if not IS_WINDOWS: return path.replace(__SPACE, __ESC_SPACE)     
    return( path if __SPACE not in path else        
            __batchOneLinerOutput( __BATCH_ESCAPE_PATH_TMPLT.format(path) ) )    

def __batchOneLinerOutput( batch ):
    cmd = __BATCH_ONE_LINER_TMPLT.format( batch )
    p = Popen( __BATCH_RUN_AND_RETURN_CMD, shell=False, 
               startupinfo=__BATCH_ONE_LINER_STARTUPINFO,
               stdin=PIPE, stdout=PIPE, stderr=PIPE )    
    # pipe cmd to stdin, return stderr, minus a trailing newline
    return p.communicate( cmd )[1].rstrip()  

アップデート

最近、Windows コンテキストのより良いトリックが思い浮かびました。短いファイル名への変換や、スペースのエスケープ シーケンスは必要ありません。あなたがする必要があるのは、コマンドの最初の文字が二重引用符であるかどうかをチェックすることによって、これにモンキー レンチを投げる Python ソースを阻止することだけです。さて、Windows cmd / Batchでは、コマンドの前に を付けて、@その行を「エコー」しないことを示すことができます。したがって、コマンドの前にそれを平手打ちするだけで、先頭の引用符はなくなります! おそらく、コマンドがエコーされることも望まないので、とにかくそれ自体が改善されています。

基本的に、__scrubSystemCmd上記を次のように置き換えます。必要に応じて、短いファイル名を取得するすべてのコードを削除できます (これは、最初に投稿したコードの半分のようなものです!)...

if IS_WINDOWS: 
    ...
    __NO_ECHO_PREFIX = "@"

def __scrubSystemCmd( cmd ):
    if IS_WINDOWS: 
        if not cmd.startswith( __NO_ECHO_PREFIX ): 
            return __NO_ECHO_PREFIX + cmd
    elif not cmd.startswith( __DBL_QUOTE ): return cmd
    cmdParts    = cmd[1:].split( __DBL_QUOTE )
    safeBinPath = _escapePath( cmdParts[0] )
    args        = __DBL_QUOTE.join( cmdParts[1:] ) # (the leading space will remain)
    return __SCRUB_CMD_TMPL.format( safeBinPath, args ) 
于 2019-01-26T16:37:43.443 に答える
0

名前にスペースが含まれるファイルの短い名前を使用できます。

file = 'C:\\Exe\\FirstV~1\\filename.exe'
os.system(file)
于 2011-08-08T02:25:48.387 に答える