如何将用户输入净化为有效的 C 字符串文字?

How can I sanitize user input into valid C-String literals?

我正在尝试使用 shell 脚本生成用于包装可执行文件的 C 代码。

这需要在 Linux 和 MacOS 上工作,并且依赖性尽可能少。我不关心 Windows(除了 WSL2)

#include <unistd.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    putenv("X=1");
    putenv("HELLO=WORLD")
    argv[0] = "/usr/bin/python3";
    return execv("/usr/bin/python3", argv);
}

天真的方法:

# make-c-wrapper.sh EXECUTABLE ARGS
#
# ARGS:
# --argv0       NAME    : set name of executed process to NAME
#                         (defaults to EXECUTABLE)
# --set         VAR VAL : add VAR with value VAL to the executable’s
#                         environment

echo "#include <unistd.h>\n#include <stdlib.h>\n\nint main(int argc, char **argv) {"
executable=""
params=("$@")

for ((n = 1; n < ${#params[*]}; n += 1)); do
    p="${params[$n]}"
    if [[ "$p" == "--set" ]]; then
        key="${params[$((n + 1))]}"
        value="${params[$((n + 2))]}"
        n=$((n + 2))
        echo "    putenv(\"$key=$value\");"
    elif [[ "$p" == "--argv0" ]]; then
        argv0="${params[$((n + 1))]}"
        n=$((n + 1))
    else
        # Using an error macro, we will make sure the compiler gives an understandable error message
        echo "    #error make-c-wrapper.sh did not understand argument $p"
    fi
done

echo "    argv[0] = \"${argv0:-$executable}\";\n    return execv(\"$executable\", argv);\n}"

但是,如果您尝试在输入中提供特殊字符,则会失败:

./make-c-wrapper /usr/bin/python3 --set "Hello" "This is\"\na test"
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    putenv("Hello=This is"
a test");
    argv[0] = "/usr/bin/python3";
    return execv("/usr/bin/python3", argv);
}

我希望在这里看到的是:

#include <unistd.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    putenv("Hello=This is\"\na test");
    argv[0] = "/usr/bin/python3";
    return execv("/usr/bin/python3", argv);
}

根据这个答案:,似乎我需要转义以下字符以确保结果是有效的 C 字符串文字:"\\r\n[=19=]\?.

有没有简单的方法来做到这一点?它需要在 MacOS 上运行,而不仅仅是 Linux.

it seems like I need to escape the following characters to make sure the result is a valid C string literal: ", \, \r, \n, [=16=] and \?.

您需要转义 "\ 和换行符。当你这样做的时候,逃避马车 return 是有意义的。虽然 ? 有一个转义序列,但该字符也可以代表它自己。输入中的空字符不能表示为字符串文字的元素,并且您的 shell 可能也不会在变量值中处理它们,因此您最好不要对它们给予任何特殊考虑。

Shell 参数扩展语法具有子字符串替换功能和您可以利用的类 C 文字语法。 shell 引用有点复杂,但是例如,这个 ...

escape_string_literal() {
    result=${1//'\'/'\'}
    result=${result//\"/'\"'}
    result=${result//$'\n'/'\n'}
    result=${result//$'\r'/'\r'}
}

escape_string_literal '"boo\"'
echo "${result}"

...打印

\"boo\\"

但是请注意,您不一定清楚在字符串文字中包含所有其他字符。特别是,即使其中大多数没有单字符转义符,其他控制字符也可能会或可能不会被接受为文字字符,具体取决于您的 C 实现。