【问题标题】:Bash doesn't think string is properly quoted?Bash 认为字符串没有被正确引用?
【发布时间】:2014-06-26 07:42:46
【问题描述】:

我正在尝试通过 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 在该命令上出错?

【问题讨论】:

  • 您是否考虑过使用shlex.quote() (Python 3.x) 或pipes.quote() (Python 2.x) 而不是自己滚动?
  • (不是说你做错了,我还没有仔细查看你的代码。但在处理转义和引用自己之前,我可能会先尝试一下)。
  • 很好的问题,但考虑添加一个净化版本的命令,你复制(ied)并粘贴(d)command 的输出,然后将其粘贴(d)到bash,它执行'。祝你好运。
  • @shellter:我不确定你要什么。我复制/粘贴到 bash 中的是问题。 (“即将运行命令”下的位,即command的输出。)
  • @LukasGraf:这个功能非常相似。我会补充一点,是的。 (我给了它一个快速测试,可悲的是,它并没有修复错误。)

标签: python bash ssh paramiko


【解决方案1】:

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

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

这是一个repr 输出,但请注意\x00x 之前的单斜杠:这是一个实际的\x00,它通过了。我的原始代码将此 Python 嵌入为 sn-p,我没有包含(我不相信它是相关的):

_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""")

【讨论】:

    【解决方案2】:

    您还需要转义换行符。更好的选择是将程序文本放在此处的文档中。

    使“About to run command:”的输出看起来像

    python -c << EOF
    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)
    .
    EOF
    

    也许你根本不需要逃避任何事情。

    【讨论】:

    • 介意添加为什么? (以及如何?)函数的一般接口只是运行一个命令;只是碰巧一个python命令暴露了这个错误。
    • bash 是面向行的。未转义的换行符终止命令。数据的第一行包含一个不匹配的刻度,从 bash 的角度来看这是一个错误。查看更新的帖子。
    • @user58697 你怎么看?单引号字符串中未转义的换行符在脚本和交互式终端中都可以正常工作。
    • 除了“那个其他人”提到的之外,这个函数需要能够执行任意命令,所以 heredocs 并不总是有效:你的答案过于关注具体的例子暴露了一些错误,并解决错误而不是修复它。
    • 我用command = commnad.replace('\n', ' ')从输入中删除了所有换行符; bash 仍然给出类似的错误。 (当然,行号会改变,但除此之外都是一样的。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-03
    • 2014-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多