I have a script I wrote for switching to root or running a command as root without a password. I edited my /etc/sudoers file so that my user [matt] has permission to run /bin/su with no password. This is my script “s” contents:
matt: ~ $ cat ~/bin/s
#!/bin/bash
[ "$1" != "" ] && c='-c'
sudo su $c "$*"
If there are no parameters [simply s], it basically calls sudo su which goes to root with no password. But if I put paramaters, the $c variable equals “-c”, which makes su execute a single command.
It works good, except for when I need to use spaces. For example:
matt: ~ $ touch file\ with\ spaces
matt: ~ $ s chown matt file\ with\ spaces
chown: cannot access 'file': No such file or directory
chown: cannot access 'with': No such file or directory
chown: cannot access 'spaces': No such file or directory
matt: ~ $ s chown matt 'file with spaces'
chown: cannot access 'file': No such file or directory
chown: cannot access 'with': No such file or directory
chown: cannot access 'spaces': No such file or directory
matt: ~ $ s chown matt 'file\ with\ spaces'
matt: ~ $
How can I fix this?
Also, what’s the difference between $* and $@ ?
Ah, fun with quoting. Usually, the approach @John suggests will work for this: use
"$@", and it won’t try to interpret (and get confused by) the spaces and other funny characters in your parameters. In this case, however, that won’t work because su’s -c option expects the entire command to be passed as a single parameter, and then it’ll start a new shell which parses the command (including getting confused by spaces and such). In order to avoid this, you actually need to re-quote the parameters within the string you’re going to pass tosu -c. bash’sprintfbuiltin can do this:Let me go over what’s happening here:
s chown matt file\ with\ spacesprintf "%q " "$@"command in the script, it replaces"$@"with the arguments to the script, with parameter breaks intact. It’s equivalent toprintf "%q " "chown" "matt" "file with spaces".printfinterprets the format string “%q ” to mean “print each remaining parameter in quoted form, with a space after it”. It prints: “chown matt file\ with\ spaces “, essentially reconstructing the original command line (it has an extra space on the end, but this turns out not to be a problem).$()construct, it’ll be treated as a single parameter to sudo). This is equivalent to runningsudo su -c "chown matt file\ with\ spaces ".sudorunssu, and passes along the rest of the parameter list it got including the fully escaped command.suruns a shell, and it also passes along the rest of its parameter list.-c:chown matt file\ with\ spaces. In the normal course of parsing it, it’ll interpret the unescaped spaces as separators between parameters, and the escaped spaces as part of a parameter, and it’ll ignore the extra space at the end.chown, with the parameters “matt” and “file with spaces”. This does what you expected.Isn’t bash parsing a hoot?