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?

Answers


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" "$@"' -- "$@"
}

Need Your Help

Parameter in a controller function returns undefined ReferenceError

asp.net-mvc angularjs

I'm going through this learning lab for AngularJS. I can get the example to work as-is (with a minor bug fix).

ThreadPool and Threading.Timer?

timer threadpool

I have a question about timer in thread?

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.