If it refers to commands run just recently, a more efficient way is to reference them with negative numbers:
!-4; !-3; !-2; !-1
Also, once you do it, your last history entry will contain the whole chain of commands, so you can repeat it with !!
.
Edit:
If you haven't already, get familiar with the great builtin function fc
, mentioned by Gilles. (Use help fc
.) It turns out that you can also use negative numbers with it, so you could do the same as above using
eval "`fc -ln -4 -1`"
This has one caveat, though: after this, the eval
line is stored in the history as the last command. So if you run this again, you'll fall into a loop!
A safer way of doing this is to use the default fc
operation mode: forwarding the selected range of commands to an editor and running them once you exit from it. Try:
fc -4 -1
You can even reverse the order of the range of commands: fc -1 -4
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.
Best Answer
With the
&&
operator between commands, each command runs in sequence, and if any command fails (i.e. returns a nonzero status), the subsequent commands are not executed.If you want to keep going no matter what, use
;
(or a newline, which is equivalent) instead of&&
. Here, you need to execute one command, and if it succeeds, execute some more commands whether they succeed or not. One way to achieve this is to put these commands inside a brace group (justcd … && mount1; mount2
won't work because this executesmount2
whether or notcd
succeeds due to precedence).Alternatively, exit the script or return from the function if
cd
fails.Alternatively, run under
set -e
, and put|| true
(“or keep going anyway”) after commands that may fail.Alternatively, write a command that must succeed: test if
/proc
and so on are mounted already.You have another problem where you call
chroot
. You've written: “run bash in the chroot. When bash exits, runsource
andexport
.” That is probably not what you meant. Reading/etc/profile
can be done by making bash a login shell. A possible way to setPS1
may be to set it before running bash, but that won't work if/etc/profile
overrides it, which is common. A better way is to setPS1
in~/.bashrc
if running inside a chroot (.bashrc
, not.profile
).Debian uses the following code to set
PS1
in/etc/bash.bashrc
based on the content of/etc/debian_chroot
:Alternatively, for the prompt, use an environment variable instead: run
and put this in
~/.bashrc
or/etc/bash.bashrc
: