如何接受以反斜杠结尾的命令行参数

时间:2009-08-18 01:00:20

标签: shell command-line

这是一个简单的Python应用程序,它只打印传入的命令行参数:

import sys
if __name__ == "__main__":
    print "Arguments:"
    for i in range(len(sys.argv)):
        print "[%s] = %s" % (i, sys.argv[i])

以下是一些示例运行:

python args.py hello world
Arguments:
[0] = args.py
[1] = hello
[2] = world

python args.py "hello world"
Arguments:
[0] = args.py
[1] = hello world

python args.py "hello\world"
Arguments:
[0] = args.py
[1] = hello\world

到目前为止一切顺利。但是现在当我以反斜杠结束任何参数时,Python就会窒息:

python args.py "hello\world\"
Arguments:
[0] = args.py
[1] = hello\world"

python args.py "hello\" world "any cpu"
Arguments:
[0] = args.py
[1] = hello" world any
[2] = cpu

我通过“r”前缀(link)了解Python不太理想的原始字符串行为,并且很明显它在这里应用了相同的行为。

但是在这种情况下,我无法控制传递给我的参数,并且我不能强制执行参数不以反斜杠结尾。我如何解决这个令人沮丧的限制?

-

编辑:感谢那些指出此行为并非特定于Python的人。它似乎是标准的shell行为(至少在Windows上,我目前没有Mac)。

更新了问题:如何接受以反斜杠结尾的args?例如,我的应用程序的一个参数是文件路径。我无法强制执行客户端发送给我,没有反斜杠,或反斜杠转义。这有可能吗?

8 个答案:

答案 0 :(得分:12)

这可能是shell将\视为转义符,从而逃避角色。所以shell发送\""(因为它认为你试图逃避双引号)。解决方案是逃避转义字符,如下所示:$ python args.py "hello\world\\"

答案 1 :(得分:7)

末尾的反斜杠被解释为转义序列的开头,在本例中是一个文字双引号字符。我在处理包含路径的环境参数时遇到了类似的问题,该路径有时以\结尾,有时则没有 我想出的解决方案是在调用可执行文件时始终在路径字符串的末尾插入一个空格。然后我的可执行文件使用带有斜杠的目录路径和末尾的空格,这将被忽略。如果导致问题,您可以修改程序中的路径。

如果%SlashPath%=“hello \”

python args.py "%SlashPath% " world "any cpu"
Arguments:
[0] = args.py
[1] = hello\ 
[2] = world
[3] = any cpu

如果%SlashPath%=“hello”

python args.py "%SlashPath% " world "any cpu"
Arguments:
[0] = args.py
[1] = hello 
[2] = world
[3] = any cpu

希望这会给你一些如何解决问题的想法。

答案 2 :(得分:6)

Microsoft参数解析规则

这些是解析由CreateProcess()传递给用C / C ++编写的程序的命令行的规则:

  1. 参数始终用a分隔 空格或制表符(多个空格/制表符 OK)
  2. 如果参数不包含 任何空格,制表符或双引号, 然后是所有人物 参数被接受(在那里 不需要包含参数 双引号)。
  3. 将空格和制表符括在一个双精度数中 引用部分
  4. 双引号部分可以是任何地方 在参数
  5. 2n反斜杠后跟“ 产生n个反斜杠+开始/结束 双引号部分
  6. 2n + 1反斜杠后跟“ 产生n个反斜杠+一个文字 引号
  7. n反斜杠后面没有 引号生成n反斜杠
  8. 如果关闭“ 立刻被另一个“,第二个” 字面上被接受并添加到 参数(这是 无证规则。)
  9. 有关详细清晰的说明,请参阅http://www.daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULESDOC

答案 3 :(得分:2)

反斜杠'逃脱'跟随它的角色。这意味着右引号成为参数的一部分,并且实际上不会终止字符串。

这是你正在使用的shell的行为(可能是bash或类似的),而不是Python(虽然你也可以在Python字符串中转义字符)。

解决方案是逃避反斜杠:

python args.py "hello\world\\"

然后,您的Python脚本应该按预期运行。

答案 4 :(得分:1)

反斜杠(\)正在逃避"。就这样。这应该如何运作

答案 5 :(得分:1)

如果这是在Windows上,那么您没有使用标准的Windows命令提示符(或shell)。这必须是bash这样做。 Windows命令提示符不会将反斜杠视为转义字符(因为它是文件路径分隔符)。

额外的琐事点:Windows命令提示中的引用字符是插入符:^

答案 6 :(得分:0)

当用户向你的函数传递一个字符串“hello \”时,无论他们的意图是什么,他们都发送了实际的字符串hello“,就像用户传递像”temp \ table“这样的文件路径他们真正输入的内容一样,无论是否有意,都是“临时能力”(中间的标签)。

这就是说,这个问题的解决方案意味着如果用户输入“temp \ table”并且老实说“临时”,你将把它处理成“temp \ table”,现在你已经以编程方式销毁了用户输入。

记住这个警告,如果您仍想这样做,您可以查找这些转义字符的字符串表示并替换它们。作为一个非常简单的例子,像这样:

def allow_tabs(str_w_tab):
    str_w_tab.replace('\t','\\t')
    print str_w_tab

现在,如果你想处理所有其他转义字符,你必须为每个字符做类似的事情。至于能够为示例执行此操作:“hello \”,用户向您传递了字符串hello“,无论他们是否有意,他们从未关闭双引号,因此这是您的程序所看到的。< / p>

答案 7 :(得分:0)

在基于'nix的系统上,这是一个基本的shell限制,就像其他人在这里所说的那样。因此,吸干它。就是说,它实际上并不那么重要,因为在这些平台上,您不需要经常在参数中使用反斜杠。

Windows 上,反斜杠至关重要。以一个结尾的路径将明确表示目录与文件。我已经看过MS C的文档(请参见:https://docs.microsoft.com/en-us/previous-versions/17w5ykft(v=vs.85))和Python源代码中的文档(例如,在subprocess.list2cmd https://github.com/python/cpython/blob/master/Lib/subprocess.py中),通过引用过程参数来解释此问题,而没有能够以反斜杠结束。因此,我原谅Python开发人员保持相同的逻辑-但不是MS C!这不是cmd.exe shell问题,也不是Windows中参数的通用限制! (插入符号^是该自然外壳中的等效转义符。)

批处理示例(test.bat):

@echo off
echo 0: %0 
echo 1: %1 
echo 2: %2
echo 3: %3 

现在执行它(通过cmd.exe):

test.bat -t "C:\test\this path\" -v

收益:

0: test.bat
1: -t
2: "C:\test\this path\"
3: -v

如您所见-一个简单的批处理文件隐式理解了我们想要的东西!

但是...让我们来看一下使用标准argparse模块(https://docs.python.org/3/library/argparse.html)时在Python中发生的情况,该模块默认与sys.argv初始解析交织在一起:

broken_args.py

import os
import argparse # pip install argparse

parser = argparse.ArgumentParser( epilog="DEMO HELP EPILOG" ) 
parser.add_argument( '-v', '--verbose', default=False, action='store_true', 
                     help='enable verbose output' )
parser.add_argument( '-t', '--target', default=None,
                     help='target directory' )                           
args = parser.parse_args()                       
print( "verbose: %s" % (args.verbose,) )
print( "target: %s" % (os.path.normpath( args.target ),) )

测试:

python broken_args.py -t "C:\test\this path\" -v

产生以下不良结果:

verbose: False
target: C:\test\this path" -v

因此,这就是我解决此问题的方法。关键“技巧”是首先通过Windows api获取该过程的完整原始命令行:

fixed_args.py

import sys, os, shlex
import argparse # pip install argparse

IS_WINDOWS = sys.platform.startswith( 'win' )
IS_FROZEN  = getattr( sys, 'frozen', False )
    
class CustomArgumentParser( argparse.ArgumentParser ):
    if IS_WINDOWS:
        # override
        def parse_args( self ):
            def rawCommandLine():
                from ctypes.wintypes import LPWSTR
                from ctypes import windll
                Kernel32 = windll.Kernel32
                GetCommandLineW = Kernel32.GetCommandLineW
                GetCommandLineW.argtypes = ()
                GetCommandLineW.restype  = LPWSTR
                return GetCommandLineW()                            
            NIX_PATH_SEP = '/'                
            commandLine = rawCommandLine().replace( os.sep, NIX_PATH_SEP )
            skipArgCount = 1 if IS_FROZEN else 2
            args = shlex.split( commandLine )[skipArgCount:]        
            return argparse.ArgumentParser.parse_args( self, args )
 
parser = CustomArgumentParser( epilog="DEMO HELP EPILOG" ) 
parser.add_argument( '-v', '--verbose', default=False, action='store_true', 
                     help='enable verbose output' )
parser.add_argument( '-t', '--target', default=None,
                     help='target directory' )                           
args = parser.parse_args()                       
print( "verbose: %s" % (args.verbose,) )
print( "target: %s" % (os.path.normpath( args.target ),) )

确认修复程序:

python fixed_args.py -t "C:\test\this path\" -v

获得这些良好的结果:

verbose: True
target: C:\test\this path