简体   繁体   中英

Escaping bash function arguments for use by su -c

I'd like to be able to write a function su_mt_user that (currently) looks like this:

su_mt_user() {
    su someuser -c "$*"
}

The objective is to be able to use it like this:

su_mt_user mt-auth --stuff etc

Which would run the command mt-auth --stuff etc as user someuser . The current version works for this particular command, but it fails for commands like this:

some_filename_with_spaces="/home/user/hello there i like spaces in filenames.txt"
su_mt_user stat "$some_filename_with_spaces"

This fails with the following errors:

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

I assume that this error happens because even though $some_filename_with_spaces is properly passed as one argument to the su_mt_user function, that function expands it to multiple arguments with "$*" .

I've also tried this, trial-and-error:

su_mt_user() {
    su someuser -c "$0 $@"
}

But that fails as well ( /usr/bin/stat: cannot execute binary file (what?))

Of course, stat "$some_filename_with_spaces" works as expected, from both the current user and the someuser user.

This looks like some escaping needs to be done, but does bash know how to do that? Does it need manual subtitution? If so, which characters need to be escaped?

To pass multiple arguments through a function to a command, you need "$@" . "$@" is special in that even though it's between double quotes, the separate arguments end up in different words, so they're passed down exactly as is. This is different from $@ or $* without quotes, which would additionally split each argument where it contains whitespace and interpret each resulting word as a glob pattern, and from "$*" , which coalesces all arguments into a single one with spaces in between.

There's an added wrinkle because su doesn't directly eat up arguments, they go through a shell. The non-option arguments to su are passed down as arguments to sh -c , and you then need an appropriate command for the -c .

su_mt_user() {
    su someuser -c '"$0" "$@"' -- "$@"
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM