Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 570
Etienne Perot Avatar asked Sep 09 '12 21:09

Etienne Perot


People also ask

How do you escape special characters in Bash?

Escape characters. Escape characters are used to remove the special meaning from a single character. A non-quoted backslash, \, is used as an escape character in Bash. It preserves the literal value of the next character that follows, with the exception of newline.

What does $@ do Bash?

bash [filename] runs the commands saved in a file. $@ refers to all of a shell script's command-line arguments. $1 , $2 , etc., refer to the first command-line argument, the second command-line argument, etc. Place variables in quotes if the values might have spaces in them.

What is $s in Bash?

From man bash : -s If the -s option is present, or if no arguments remain after option processing, then commands are read from the standard input. This option allows the positional parameters to be set when invoking an interactive shell. From help set : -e Exit immediately if a command exits with a non-zero status.

How do you escape a colon in Bash?

In bash ; escape with '\'. You do not need to escape the colon; assuming the variable values are correct, quoting the expansions is the only thing that may make a difference.


1 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" "$@"' -- "$@"
}
like image 189
Gilles 'SO- stop being evil' Avatar answered Oct 03 '22 01:10

Gilles 'SO- stop being evil'