Some time ago I came accross this.

While an alias like sudo $(history -p \!\!) can be very useful, there are some problems with it. First, it splits up parameters that have spaces in them, effectively breaking the command. Second, it doesn't do parameter substitution, so it might again pass the wrong parameters to your command.

To illustrate I use this simple python script:

#!/usr/bin/env python3
# test.py

import sys

for a in sys.argv:                    # for each command line parameter
    print('<arg>{0}</arg>'.format(a)) # print the parameter surrounded by 'arg' tags
print()                               # add an extra newline at the end of the output

And I ran these commands:

halves@pc > alias oops='sudo $(history -p \!\!)'
halves@pc > t='foo bar'
halves@pc > ./test.py * "$t" "$(echo quux)" file\ name\ with\ spaces
<arg>./test.py</arg>
<arg>1</arg>
<arg>2</arg>
<arg>3</arg>
<arg>file name with spaces</arg>
<arg>test.py</arg>
<arg>test.sh</arg>
<arg>foo bar</arg>
<arg>quux</arg>
<arg>file name with spaces</arg>

halves@pc > oops
[sudo] password for halves:
<arg>./test.py</arg>
<arg>1</arg>
<arg>2</arg>
<arg>3</arg>
<arg>file name with spaces</arg>
<arg>test.py</arg>
<arg>test.sh</arg>
<arg>"$t"</arg>
<arg>"$(echo</arg>
<arg>quux)"</arg>
<arg>file\</arg>
<arg>name\</arg>
<arg>with\</arg>
<arg>spaces</arg>

As you can see here, the arguments when using oops are different. Specifically, the "$t" is passed without substitution, "$(echo quux)" is split up and passed without substitution and file\ name\ with\ spaces is split up. The filename with spaces stays intact when passed as a part of * though.

To solve these problems, `oops' has to become a bit more complex. In fact, it had to become a function instead of an alias. The function and the same test as I ran for the alias are shown below.

halves@pc > declare -f oops
oops()
{
	# create a temporary file
	local f="$(mktemp)"
	# write "sudo " to the file without a trailing newline
	echo -n "sudo " > "$f"
	# append the previous command from the history to the file
	history -p !-1 >> "$f"
	# run the command in this shell so it has access to the same
	# environment. Also, this way the command will be parsed again in
	# the same way it was done before, so the parameters will be
	# interpreted the same way
	source "$f"
	# store the return value in a local variable
	local r="$?"
	# remove the temporary file
	/bin/rm "$f"
	# return the returnvalue from the command in the temporary file
	return "$r"
}
halves@pc > t='foo bar'
halves@pc > ./test.py * "$t" "$(echo quux)" file\ name\ with\ spaces
<arg>./test.py</arg>
<arg>1</arg>
<arg>2</arg>
<arg>3</arg>
<arg>file name with spaces</arg>
<arg>test.py</arg>
<arg>test.sh</arg>
<arg>foo bar</arg>
<arg>quux</arg>
<arg>file name with spaces</arg>

halves@pc > oops
[sudo] password for halves:
<arg>./test.py</arg>
<arg>1</arg>
<arg>2</arg>
<arg>3</arg>
<arg>file name with spaces</arg>
<arg>test.py</arg>
<arg>test.sh</arg>
<arg>foo bar</arg>
<arg>quux</arg>
<arg>file name with spaces</arg>

As you can see, this time results of the original command and oops are exactly the same, with the exception of sudo being used in the latter.

To use the improved version just add this to your .bashrc:

oops()
{
	local f="$(mktemp)"
	echo -n "sudo " > "$f"
	history -p !-1 >> "$f"
	source "$f"
	local r="$?"
	/bin/rm "$f"
	return "$r"
}