Skip to main content
 首页 » 编程设计

python之Bash 认为字符串没有被正确引用

2025年05月04日10shangdawei

我正在尝试通过 SSH 执行命令,但另一端的 bash 认为它没有正确转义。

这里,self._client是一个paramiko.SSHClient对象; args 是一个参数列表,即要执行的命令。

def run(self, args, stdin=None, capture_stdout=False): 
    """Runs a command. 
 
    On success, returns the output, if requested, or None. 
 
    On failure, raises CommandError, with stderr and, if captured, stdout, 
    as well as the exit code. 
    """ 
    command = ' '.join(_shell_escape(arg) for arg in args) 
    print('About to run command:\n  {}'.format(command)) 
    print('About to run command:\n  {!r}'.format(command)) 
    channel = self._client.get_transport().open_session() 
    channel.exec_command(command) 

_shell_escape:

_SHELL_SAFE = _re.compile(r'^[-A-Za-z0-9_./]+$') 
def _shell_escape(s): 
    if _SHELL_SAFE.match(s): 
        return s 
    return '\'{}\''.format(s.replace('\'', '\'\\\'\'')) 

我试图通过它运行一些 Python。在 stderr 上,我返回:

bash: -c: line 5: unexpected EOF while looking for matching `'' 
bash: -c: line 6: syntax error: unexpected end of file 

两个打印语句的输出:

About to run command: 
  python -c 'import os, sys 
path = sys.argv[1] 
if sys.version_info.major == 2: 
    path = path.decode('\''utf-8'\'') 
entries = os.listdir(path) 
out = b'\'''\''.join(e.encode('\''utf-8'\'') + b'\'''\'' for e in entries) 
sys.stdout.write(out) 
' . 
About to run command: 
  "python -c 'import os, sys\npath = sys.argv[1]\nif sys.version_info.major == 2:\n    path = path.decode('\\''utf-8'\\'')\nentries = os.listdir(path)\nout = b'\\'''\\''.join(e.encode('\\''utf-8'\\'') + b'\\''\x00'\\'' for e in entries)\nsys.stdout.write(out)\n' ." 

如果我复制并粘贴 command 的输出,并将其粘贴到 bash 中,它就会执行,所以它确实看起来确实被正确转义了。我目前的理解是,在另一端,SSH 将接受命令并运行 [my_shell, '-c', command]。

为什么 bash 在该命令上出错?

请您参考如下方法:

输入包含一个嵌入的 nul 字符,bash 似乎将其视为字符串的结尾。 (我不确定有什么办法不能!)。这在我的问题中可见,我在其中输出 command:

About to run command: 
  "python -c 'import os, sys [SNIP…] + b'\\''\x00'\\'' for [SNIP…]" 

这是一个 repr 输出,但请注意 \x00 中 x 之前的单斜杠:这是一个实际的 \x00 让它通过了。我的原始代码将这个 Python 作为片段嵌入,但我没有包含它(我认为它不相关):

_LS_CODE = """\ 
import os, sys 
path = sys.argv[1] 
if sys.version_info.major == 2: 
    path = path.decode('utf-8') 
entries = os.listdir(path) 
out = b''.join(e.encode('utf-8') + b'\x00' for e in entries) 
sys.stdout.write(out) 
""" 

在这里,Python 的 """ 仍在处理 \ 作为转义字符。我需要加倍,或查看原始字符串 (r""")