转义bash函数参数以供su -c使用

Eti*_*rot 5 bash arguments escaping function su

我希望能够编写一个su_mt_user(当前)看起来像这样的函数:

su_mt_user() {
    su someuser -c "$*"
}
Run Code Online (Sandbox Code Playgroud)

目标是能够像这样使用它:

su_mt_user mt-auth --stuff etc
Run Code Online (Sandbox Code Playgroud)

哪个会mt-auth --stuff etc以用户身份运行该命令someuser.当前版本适用于此特定命令,但对于以下命令失败:

some_filename_with_spaces="/home/user/hello there i like spaces in filenames.txt"
su_mt_user stat "$some_filename_with_spaces"
Run Code Online (Sandbox Code Playgroud)

此操作失败,并出现以下错误:

stat: cannot stat '/home/user/hello': No such file or directory
stat: cannot stat 'there': No such file or directory
stat: cannot stat 'i': No such file or directory
stat: cannot stat 'like': No such file or directory
stat: cannot stat 'spaces': No such file or directory
stat: cannot stat 'in': No such file or directory
stat: cannot stat 'filenames.txt': No such file or directory
Run Code Online (Sandbox Code Playgroud)

我假设发生此错误$some_filename_with_spaces是因为即使作为一个参数正确传递给su_mt_user函数,该函数也会将其扩展为多个参数"$*".

我也试过这个,试错了:

su_mt_user() {
    su someuser -c "$0 $@"
}
Run Code Online (Sandbox Code Playgroud)

但那也失败了(/usr/bin/stat: cannot execute binary file(什么?))

当然stat "$some_filename_with_spaces",从当前用户和someuser用户都可以按预期工作.

这看起来像是需要做一些逃避,但是bash知道如何做到这一点吗?是否需要手动替代?如果是这样,哪些角色需要转义?

Gil*_*il' 11

要通过函数将多个参数传递给命令,您需要"$@"."$@"特别之处在于即使它在双引号之间,单独的参数也以不同的词结尾,所以它们完全按原样传递.这与引号不同$@$*不引用,它们会另外将每个参数拆分为包含空格并将每个结果单词解释为glob模式,并且从中"$*"将所有参数合并为单个参数,其间包含空格.

还有一个额外的皱纹,因为su不直接吃论点,他们通过壳.非选项参数su作为参数传递给sh -c,然后您需要一个适当的命令-c.

su_mt_user() {
    su someuser -c '"$0" "$@"' -- "$@"
}
Run Code Online (Sandbox Code Playgroud)