为csh引号字符串

0 投票
1 回答
580 浏览
提问于 2025-04-17 18:20

在这个问题中,当我提到“csh”时,其实是指tcsh。

我知道一般建议在编程时避免使用csh。不过,有时候我们需要处理已有的csh代码,这时就需要为csh格式化一个字符串。换句话说,问题是如何在csh的语法中表示一个任意的字节字符串。

下面这个csh_escape_arg函数是否正确?也就是说,是否存在一个字符串,如果把它加到测试的字符串列表中,会导致测试失败?如果有这样的字符串,我该如何修改我的函数,确保所有字符串都能通过测试呢?

import string
import subprocess
import unittest

# Safe unquoted
_safechars = frozenset(string.ascii_letters + string.digits + '@%_-+:,./')

def csh_escape_arg(str_):
    """Return a representation of str_ in csh.

    Based on the standard library's pipes.quote
    """
    for c in str_:
        if c not in _safechars:
            break
    else:
        if not str_:
            return "''"
        return str_
    str_ = str_.replace("\\", "\\\\")
    str_ = str_.replace("\n", "\\\n")
    str_ = str_.replace("!", "\\!")
    # use single quotes, and put single quotes into double quotes
    # the string $'b is then quoted as '$'"'"'b'
    return "'" + str_.replace("'", "'\"'\"'") + "'"

def csh_escape(args):
    return " ".join(csh_escape_arg(arg) for arg in args)

def get_cmd_stdout(args, **kwargs):
    child = subprocess.Popen(args, stdout=subprocess.PIPE, **kwargs)
    stdout, stderr = child.communicate()
    rc = child.returncode
    if rc != 0:
        raise Exception("Command failed with return code %d: %s:\n%s" % (rc, args, stderr))
    else:
        return stdout

class TestCsh(unittest.TestCase):

    def test_hard_cases(self):
        for angry_string in [
            "\\!\n\"'`",
            "\\\\!\n\"'`",
            "=0",
            ]:
            out = get_cmd_stdout(["tcsh", "-c", csh_escape(["echo", "-n", angry_string])])
            self.assertEqual(out, angry_string)

unittest.main()

1 个回答

2

1) 对于tcsh,你还需要把“=”用引号括起来,这样才能防止目录栈的替换。

2) 我觉得你的算法在处理带有未配对双引号的字符串时也会遇到问题。

3) 另外一个办法是把你的目标脚本写成这样,让字符串不被替换。比如,可以先把字符串写入一个文件,然后让你的脚本从这个文件中读取字符串到一个变量里,像这样:

set a = `cat file`

然后根据需要使用这个变量。

撰写回答