Polite Bash Commands
Published 5 years, 4 weeks pastFor years, I’ve had a bash alias that re-runs the previous command via sudo. This is useful in situations where I try to do a thing that requires root access, and I’m not root (because I am never root). Rather than have to retype the whole thing with a sudo on the front, I just type please and it does that for me. It looked like this in my .bashrc file:
alias please='sudo "$BASH" -c "$(history -p !!)"'
But then, the other day, I saw Kat Maddox’s tweet about how she aliases please straight to sudo, so to do things as root, she types please apt update, which is equivalent to sudo apt update. Which is pretty great, and I want to do that! Only, I already have that word aliased.
What to do? A bash function! After commenting out my old alias, here’s what I added to .bash_profile:
please() {
if [ "$1" ]; then
sudo $@
else
sudo "$BASH" -c "$(history -p !!)"
fi
}
That way, if I remember to type please apachectl restart, as in Kat’s setup, it will ask for the root password and then execute the command as root; if I forget my manners and simply type apachectl restart, then when I’m told I don’t have privileges to do that, I just type please and the old behavior happens. Best of both worlds!
Comments (7)
Very cool! Time to be polite to AI. LOL!
Hi. Unfortunately, using
sudo $BASH -clike that can sometimes run a different command to usingsudoin the first place, because your command line is now being interpreted inroot‘s environment rather than your own.For a trivial demo try:
It will affect any environment variable that you and
roothave set differently, and any path starting with~/— which will lead either to an error message (root‘s home directory doesn’t contain a file with that name) or, worse, operating on the wrong file (you’ve just copied an SSH key toroot‘s.ssh/directory rather than your own).To avoid this, instead of invoking a nested Bash process, use
evalin your existing one:For what it’s worth, my variant of this function also manipulates Bash history to insert the command you actually wanted to run (so if you want to restart Apache again, pressing
Upwill give yousudo apachectl restart, rather thanplease). I’m less polite than you, so mine is calledrr, for “redo as root”:function rr # redo as root — repeat the previous command with sudo; credit: # https://twitter.com/liamosaur/status/506975850596536320 { cmd=$(fc -ln -1) cmd="sudo ${cmd#* }" echo "$cmd" # Append the sudo-ed command to this shell's history, so that the Up arrow # can be used to find it, rather than just the rr command. Weirdly this seems # to cause rr itself not to end up in the history, which is an unexpected # bonus: history -s "$cmd" # The quotes are needed to preserve any quotes in $cmd, and eval to parse # them: eval "$cmd" }That’s literally a tab and a space in the substitution line with a big gap in it. Omit the
echoline if you don’t want the command being run to be displayed first.Er… it seems like you guys are making this way more complicated than it needs to be. Just use
!!directly.Do you know the command : sudo !!
This put sudo in front of your previous command
The same thing as your alias
Not clear to me why you don’t just use
sudo $(history -p !!)instead ofsudo "$BASH" -c "$(history -p !!)"to avoid spinning up a shell with root permissions.Btw. instead of
historyyou can also make use of thefcbuiltin. I.e.sudo $(fc -ln 0).David, just doing
sudo $(history -p !!)will fail if, for instance, the previous command has quote marks in it, or refers to a$FOOenvironment variable, or uses~for a home directory, or anything else which is interpreted by the shell.The command line needs interpreting with Bash before running the command, which
$(...)doesn’t do — it just passes the text as arguments tosudoliterally.I’ve always used
↑^asudo ⏎to go to the previous line and add sudo at the beginning and run it. That way if I want to re-run the command in the future, I can find it by going up and I don’t have to run two commands in order.