Monday 14 November 2016

Re-executing shell commands from history


The ability to re-execute a previously typed command can be very useful if the concerned command was a  long one like a complex find search. Bash's ability to store command history is indispensable to any system administrator. In this article, I'll briefly showcase tapping into the shell history to avoid re-typing commands.

Before getting to the commands I would like to mention that the history of shell commands you typed during a session is written to your .bash_history file only after you've gracefully terminated your session. Before that the command history is stored in memory. So, in case you are working on the command line & someone restarts the system or there is a panic event, your session command history will probably not be written to .bash_history file & you won't be able to retrieve it for future reference.

With that gotcha said & done let's get started with the 'how to's'.

To re-execute a command you just executed, press the up arrow key, you'll see the command again & press enter. You can also type !! on the command line to re-execute the last command you type. For example:

[root@cserver ~]# uname -a
Linux cserver 3.10.0-123.el7.x86_64 #1 SMP Mon Jun 30 12:09:22 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
[root@cserver ~]#
[root@cserver ~]# !!
uname -a
Linux cserver 3.10.0-123.el7.x86_64 #1 SMP Mon Jun 30 12:09:22 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
[root@cserver ~]#
[root@cserver ~]#


Another interesting feature available in bash while executing the last executed command is that we can change the characters/words that were part of the previous command.
For example, we ran a find command on a directory location & we want to run the find command again but on a different directory. Instead of typing the complete command again we can use bash's built in features to just substitute the new directory location & execute find again.

In order to replace a single character like a 1 with a 2, we use the following expression on the command line:

!:gs/1/2/ [!:gs/old/new/]

If we want to replace an entire word, we use the following expression:

^old^new

Here are a couple of examples to demonstrate this:

[sahil@localhost ~]#touch test1 user1
[sahil@localhost ~]#!:gs/1/2/
touch test2 user2
[sahil@localhost ~]#^test^t
touch t2 user2


Another quick trick comes to mind in case we need to re-execute a command with arguments. In case we want to repeat the last command but with a different set of arguments we can use !:0 !:<arg_num> where arg_num is argument number on the command line. Her's an example:

[sahil@local ~]#echo a b c d
a b c d
[sahil@local ~]#!:0 !:3
echo c
c
[sahil@local ~]#!:0 e
echo e

e


To move through the history further use the up arrow key to move up & the down arrow key to move down in the history of previously executed shell commands.
We can type history followed by a number to display the number of commands in descending order beginning with the last command going up. For example, typing history 10 will display the last 10 commands that were executed.
When we run the history command it shows us list of available commands in history preceded by a number. If we wish to execute a command at a particular number, we just need to type the ! (bang symbol) followed by the number to re-run the command.

In the below example, I've re-executed the uname -a command by typing the command number preceded by the ! symbol.

[root@cserver ~]# history 10
  204  ./congif -l 0  -o fast.gif foo.t foo.d
  205  ls
  206  rm foo.d foo.t
  207  mv fast.gif ~
  208  ls
  209  history
  210  uname -a
  211  cd
  212  uname -a
  213  history 10
[root@cserver ~]#
[root@cserver ~]# !212
uname -a
Linux cserver 3.10.0-123.el7.x86_64 #1 SMP Mon Jun 30 12:09:22 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
[root@cserver ~]#


Now, we seen how we can re-execute the last command with !! & the nth command in shell history via !n. But what if I want to execute commands number 150 through 200 again.
We do not need to type !n fifty times! For such cases we use the fc command.

Here's how it works. We just type fc followed by the first & last numbers of the history commands that we'd like the shell to re-run. This will open up a tmp file with our selected commands typed in. We can make changes to any of the commands as we see fit & then do :wq! as we do to save changes to a file edited with vi editor. As soon as the file closes, the selected commands will be re-executed on the terminal.

Demo:

In this demonstration, I've selected commands 52 to 57 by typing fc 52 57, saved the contents of the tmp file generated by typing :wq! & the commands get executed as soon as the file closes.



No comments:

Post a Comment

Using capture groups in grep in Linux

Introduction Let me start by saying that this article isn't about capture groups in grep per se. What we are going to do here with gr...