Execute a line of commands with one sudo

33,237

Solution 1

You can't just stick sudo in front of a shell command, you have to invoke a shell to evaluate that command again (doing things like expanding variables, opening files for redirection operators, etc.). So that's

sudo bash -c !!

except that this doesn't quite work, because !! interpolates the text of the previous command, special characters and all. You need to retrieve the text of the command as a string and pass that as an argument to sh. Fortunately, bash's fc builtin lets you do that¹.

sudo bash -c "$(fc -ln -1)"

Or even, to be sure to invoke the same version of bash that's currently running:

sudo "$BASH" -c "$(fc -ln -1)"

Note that since the command is executed in a separate shell process, it inherits environment variables (only the ones that sudo preserves, mind), but not shell internal variables. Shell options (e.g. kshglob) and other settings will start from the default either.

The same command² works in zsh and ksh, though ATT ksh93 requires both the first and last number to be passed to fc³ (which also works in bash, zsh and pdksh/mksh):

sudo zsh -c "$(fc -ln -1)"
sudo ksh -c "$(fc -ln -1 -1)"
sudo "$0" -c "$(fc -ln -1 -1)"

Using $0 to designate the executable of the running shell works only if the shell was invoked through the $PATH and the $PATH hasn't changed, or through an absolute path.

Here's another method in zsh which is slightly clearer but longer:

sudo zsh -c $history[$[HISTCMD-1]]

A final word of warning: sudo is for potentially dangerous commands. Don't make it too easy to use it!

¹ There's some extra whitespace at the beginning and the command substitution strips off newlines at the end, but the syntax of the shell doesn't care about that.
² I don't think zsh or ksh have anything like bash's $BASH; $0 only works when it's an absolute path, or when it contains no slash and the command search path hasn't changed.
³ fc is an alias for hist in ATT ksh but that's just as good.

Solution 2

If you want to redo the same command with sudo !! after doing a command like this:

echo "something">/path/file

The you use the global replace syntax to recall the command :

!!:gs/>/|sudo tee -a /

Use a space after the -a parameter.

This is the equivalent to sudo !! but helps you bypass sudo restrictions for the < and >. Because sudo does not allow you to use [<, >].


To bypass sudo restrictions in general for redirections you can use it like this:

echo "something" | sudo tee myfile

tee command will allow you to read from standard input and write to standard output and files

If you want to repeat the command and append a text to the file, the tee command has the -a option for appending. So you could recall the command with

sudo !!

and the text will be appended in the file

example:

echo "something" | sudo tee -a /path/file
sudo !!

Solution 3

It seems to be as simple as sudo sh -c "!!":

$ cd /
$ echo hello > foo
bash: foo: Permission denied
$ sudo sh -c "!!"
sudo sh -c "echo hello > foo"
$ ls -l foo
-rw-r--r-- 1 root root 6 Jun 20 16:21 foo
Share:
33,237

Related videos on Youtube

rubo77
Author by

rubo77

SCHWUPPS-DI-WUPPS

Updated on September 18, 2022

Comments

  • rubo77
    rubo77 almost 2 years

    For example if I want to create a file and enter text in one line I can redirect the output into a file with the use of the > operator:

    echo "something" > /path/foobar
    

    but if I don't have access to the folder /path/ and need sudo priviledges, How can I achieve this same command as a normal user with sudo rights?

    I tried

    sudo echo "something" > /path/foobar
    

    but that doesn't work, because the sudo only counts for the edit but not for the right part
    of the >

    Sure, I could become root before with sudo su or use tee instead:

    echo "something" | sudo tee /path/foobar
    

    But I would like to find a solution where I can work with the last line as replacement via

    sudo !!
    

    Isn't there a way to "recycle" the last line and just add sudo in front?

    • goldilocks
      goldilocks about 10 years
    • rubo77
      rubo77 about 10 years
      This is not a duplicate. I extracted the real Qeustion and changed the title. Now it is what I am really looking for
    • goldilocks
      goldilocks about 10 years
      It certainly was a duplicate, but it is somewhat different now. It looks to me like you rewrote the question in light of Boogy's answer, which is fine -- that is a good answer. I've retracted my close vote, but it is still inevitable (people often vote automatically once a question is in the close queue without paying attention to the issue): address a comment @goldilocks when it gets closed and I'll vote to reopen. You can also plead your case in chat once that happens.
    • Gilles 'SO- stop being evil'
      Gilles 'SO- stop being evil' about 10 years
  • rubo77
    rubo77 about 10 years
    nice solution, but a bit hard to memorize. How can I create an alias for a command using !! to recall the last command?
  • godlygeek
    godlygeek about 10 years
    If the command used quotes, this isn't necessarily equivalent. Consider echo "foo bar" which would become sh -c "echo "foo bar"" which would just print foo instead of foo bar.
  • rubo77
    rubo77 about 10 years
    is the -a really needed? > will overwrite a file, tee -a will append, which would be >>
  • rubo77
    rubo77 about 10 years
    I tried sudo sh -c '!!' instead if your command but that doesn't work either
  • godlygeek
    godlygeek about 10 years
    Then what if your command used both single and double quotes? Or double quotes and variable expansions? If the user did: a=b and then echo "$a" >file and then sudo sh -c '!!' it would expand to sudo sh -c 'echo $a >file' which would print an empty line instead of b into file.
  • rubo77
    rubo77 about 10 years
    So maybe there is an even better solution somehow?
  • godlygeek
    godlygeek about 10 years
    I don't think there's any solution that will work 100% of the time without you ever needing to modify the command. Using sudo to get a root shell and then copying and pasting the command into it would be closest, but fails if the command was expected to use variables that were set in your non-root shell. Using sudo sh -c "!!" works fine for simple commands, but is fraught for complex ones. I think that the best advice is to not expect that there is some command that will work 100% of the time - learn some possible solutions and apply each when it best fits.