Softpanorama

May the source be with you, but remember the KISS principle ;-)
Home Switchboard Unix Administration Red Hat TCP/IP Networks Neoliberalism Toxic Managers
(slightly skeptical) Educational society promoting "Back to basics" movement against IT overcomplexity and  bastardization of classic Unix

Bang commands in bash

Version 2.5 (Jun 8, 2021)

News See Also Recommended Links Reference Bang commands Bang arguments
 History Controls Searching history Event Designators Word Designators Modifiers Examples
Command completion Readline and inputrc Input and Output Redirection Customizing Shell Dot Files: .profile, RC-file, and history Examples of .bashrc files Shell Prompts Customarization
Pushd, popd and dirs as a proxy for "cd history" Orthodox File Manager Paradigm. Ch. 4 Norton Change Directory (NCD) clones Advanced filesystem navigation

fc command


Care and Feeding of Functions in Shell bash Tips and Tricks Unix shells history Sysadmin Horror Stories Humor Etc
 
"Those who cannot remember the past are condemned to repeat it."
 

George Santayana (1863-1952)

Most Unix shells including bash have some interesting extensions of the command history called bang commands. They allow you not only quickly retrieve several most recent commands but also reuse parts of previous command (or any command within history). Their role now is diminished as they are difficult to remember, and many of those manipulation can be done by retrieving the command from history and using keyboard and mouse to modify it. Still several of them retain their usefulness.

I think you need  to know two most often used bang commands (!!, and !$). It's actually difficult to remember more than that as their usage is pretty infrequent. Both !! (previous command  -- usually in the idiom sudo !! and !$ (the last operand of the previous command) are extremely useful.

More convoluted bang operations are often simpler to perform just by listing history and selecting the necessary substring using the mouse.  but sometimes you do not have mouse capability (for example, in some trainings courses which is paranoid about intellectual property)

Replacement  can also sometimes be very useful, when the replacement string is deep inside previous command and you can't just erase tail and type the necessary replacement string. Here is one,  not very convincing but simple example from  Stupid Bash tricks- History, reusing arguments, files and directories, functions, and more - Enable Sysadmin

Let's say I run the following command:

$> sudo systemctl status sshd

Bash tells me the sshd service is not running, so the next thing I want to do is start the service. I had checked its status with my previous command. That command was saved in history, so I can reference it. I simply run:

$> !!:s/status/start/
sudo systemctl start sshd

The above expression has the following content:

The result is that the sshd service is started.

Next, I increase the default HISTSIZE value from 500 to 5000 by using the following command:

$> echo "HISTSIZE=5000" >> ~/.bashrc && source ~/.bashrc

What if I want to display the last three commands in my history? I enter:

$> history 3
 1002  ls
 1003  tail audit.log
 1004  history 3

Most important subset of band commands

Bang commands are remnant of the era when typewriters were used as terminals but they proved to be used in CRT world too. There are way too many bang command to remember them all. But several most common cases should in the arsenal of any sysadmin, who respects himself. Because for various reason mouse some day might not be available. also some of those command are quicker then using mouse.  I would outline seven most important usage cases:  

  1. !! --Will execute the last command. Same as   "up-arrow + return". The most common use is sudo !! when issued command failed due to insufficient privileges.
  2. !gzip  -- will re-execute the most recent gzip command (history is searched  in reverse order). String specified is considered to be the prefix of the  command.
  3. !?etc.gz – same as above but the unique string doesn't have to be at the start of the command. That means that you can identify command by a unique string appearing anywhere in the command  (might be faster then Ctrl-R  mechanism)
What is more important and convenient is that you can extract arguments from previous commands  (see below). for example:
  1. !1 designates the first argument of the last command.  
  2. !$ designates the last argument of the preceding command. 

 There are a couple of useful idioms:

 More about retrieving previous command arguments

For hardcore system which  despise mouse we will provide more complete overview of capabilities of band command. And as we mentioned before even for regular admin  who use mouse there are cases when make sense to retrieve the previous commands arguments using so called word designators or bang arguments.  We listed some above but there might be more for you and you can discover them analyzing the capabilities listed below.

Generally a colon (:) separates the event designator and the word designator, but it can be omitted, if the word designator begins with $, %, ^, *, or - . As well for for command line arguments from 1 to 9 ($1-$9) 

If you want the word to be selected from the previous command line, the second ! is not required. For example, !!:12 and !:12 both refer to the 12-th word on the previous command. Moreover for arguments 1-9 you can omit the comons altotheres using !1-!9 notation. 

You can address the necessary argument by using words, lines count from the last and search strings

Here is a more compete reference:

Event Designators

An event designator references a command line stored in the history file. There are several formats of event designators, they are as follows.

! Start a history substitution command, except when followed by a space, tab, new-line, =, or (.
!! Reference the previous command line. Typing !! and pressing Return executes the previous command.
!n Reference command line with event number n from the history file. This references a specific event number. For example, if the current event number is 7, you can specify an event number from 1 to 6. The command !3 would reexecute event 3, vi wsr.092989.
!-n Reference the nth previous command event. The current event number -n. For example, if the current event number is 7 and you type !-3, event number 4 is executed.
!str Reference the most recent command line starting with string str. For example, !vi reexecutes the "vi apiprog.c" command.
!?str[?] Reference the most recent command line containing the string str. For example, !?dly reexecutes the "find ..." command.
!{str} Insulate a reference string from adjacent characters.

Word Designators

Word designators provide a way to reference specific arguments (words) from a command line stored in the history file. A colon (:) separates the event designator and the word designator.

NOTES:

  1. If the word designator begins with $, %, ^, *, or - the : is not needed.
  2. If you want the word to be selected from the previous command line, the second ! is not required. For example, !!:2 and !:2 both refer to the second word on the previous command.
  3. Words are zero relative, that is, word zero is the command itself. The first argument (arg1) on the command line is word 1.

The available word designators are as follows (described for the previous command, replace with !! for others):

Modifiers

You can add modifiers after the word designators. A modifier is used to modify the referenced command line. Each modifier must be preceded with a colon (:).

It make sense to try command with modifier :p in case the command is destructive (like rm and find ... -exec rm...  commands are) Suffix :p allows to print a command, but not execute it. Another three useful modifiers are:

Let's say I'm reading a file nested deeply in a directory structure. When I finish editing the file, I realize that there are some other operations I want to do in that directory and that they would be more easily accomplished if I were in that directory. I can use :h to help get me there.

links /usr/local/share/doc/3dm/3DM_help.htm
cd !$:h
links !-2 $:t

As we see !$ can be modified by :h.

In our next example, we've downloaded a tarball from the Internet create directory with the name of the tarball and unpack the tarball into it. We check

wget http://www.example.com/path/to/test.tgz
mkdir !$:r
cd !$
tar xzvf ../test.tgz

The second command will create a directory called 'test' in the current directory.

Word modifiers can be stacked as well. Here is more complex example, in which we'll first download a file to /tmp, and then create a directory for the contents of that tar file in /usr/local/src and then unpack the tarball into it:

cd /tmp
wget http://www.example.com/path/KickassApplicationSuite.tar.gz
cd /usr/local/src/
mkdir !-2$:t:r:r
{creates directory called 'KickassApplicationSuite'}
cd !$
tar xvzf /tmp/!-4$:t

The first three commands are fairly common and use no substitution. The fourth command, however, seems like gibberish. We know !-2 means the command prior to the most recent one and that $ indicates the last argument of that command. We even know that :t will strip off the path portion of that argument (in this case, even the "http://".) We even know that :r will remove the file-extension to that argument, but here we call it twice, because there are two extensions (.gz is removed by the first :r and .tar is removed by the second.) We then cd into that directory (!$ again, is the argument to the previous command, in this case the argument to mkdir, which is 'KickassApplicationSuite'.)

We then untar the file. !-4$ is the last argument to the command four commands ago, which is then modified by :t to remove the path, because we added the path as /tmp/. So the last command becomes tar xvzf /tmp/KickassApplicationSuite.tar.gz.

There's even a word modifier for substitution. :s can be used similarly to circumflex hats to do simple line substitution.

 vi /etc/X11/XF86config
!!:s/config/Config-4/

We know that !! means the previous command string. :s modifies the previous command, substituting the first argument to :s with the second argument to :s. My example used / to delimit the two arguments, but any non-whitespace character can be used. It's also important to note that, just like circumflex hat substitution, the substitution will only take place on the first instance of the string to be substituted. If you want to affect every instance of the substitution string, you must use the :g word modifier along with :s.

 mroe file1 ; mroe file2
!!:gs/mroe/more

The second command substitutes (:s) more for all (:g) instances of mroe. Hint: :g can be used with circumflex hats too!

The final word modifier we'll look at in this tutorial is &. & means repeat the previous substitution. Let's say we're examining file attributes with the ls command.

ls -lh myfile otherfile anotherfile
!!:s/myfile/myfile.old/

Seems simple enough. :s steps in and changes myfile to myfile.old so we end up with ls -lh myfile.old myfile2 myfile3. & is just a shortcut that we can use to represent the first argument to :s The following example is equivalent to the example above:

ls -lh myfile otherfile anotherfile
!!:s/myfile/&.old/

History Editing Examples

The following examples illustrate how to use the Event Designators, Word Designators, and the Modifiers.


Top Visited
Switchboard
Latest
Past week
Past month

NEWS CONTENTS

Old News ;-)

[Jun 08, 2021] Bang commands: two potentially useful shortcuts for command line -- !! and !$ by Nikolai Bezroukov

softpanorama.org

Those shortcuts belong to the class of commands known as bang commands . Internet search for this term provides a wealth of additional information (which probably you do not need ;-), I will concentrate on just most common and potentially useful in the current command line environment bang commands. Of them !$ is probably the most useful and definitely is the most widely used. For many sysadmins it is the only bang command that is regularly used.

  1. !! is the bang command that re-executes the last command . This command is used mainly as a shortcut sudo !! -- elevation of privileges after your command failed on your user account. For example:

    fgrep 'kernel' /var/log/messages # it will fail due to unsufficient privileges, as /var/log directory is not readable by ordinary user
    sudo !! # now we re-execute the command with elevated privileges
    
  2. !$ puts into the current command line the last argument from previous command . For example:

    mkdir -p /tmp/Bezroun/Workdir
    cd !$
    
    In this example the last command is equivalent to the command cd /tmp/Bezroun/Workdir. Please try this example. It is a pretty neat trick.

NOTE: You can also work with individual arguments using numbers.

For example:
cp !:2 !:3 # picks up  the first and the second argument from the previous command
For this and other bang command capabilities, copying fragments of the previous command line using mouse is much more convenient, and you do not need to remember extra staff. After all, band commands were created before mouse was available, and most of them reflect the realities and needs of this bygone era. Still I met sysadmins that use this and some additional capabilities like !!:s^<old>^<new> (which replaces the string 'old' with the string 'new" and re-executes previous command) even now.

The same is true for !* -- all arguments of the last command. I do not use them and have had troubles writing this part of this post, correcting it several times to make it right 4/0

Nowadays CTRL+R activates reverse search, which provides an easier way to navigate through your history then capabilities in the past provided by band commands.

Recommended Links

Google matched content

Softpanorama Recommended

Top articles

Sites

Softpanorama

May the source be with you, but remember the KISS principle ;-)
Home Switchboard Unix Administration Red Hat TCP/IP Networks Neoliberalism Toxic Managers
(slightly skeptical) Educational society promoting "Back to basics" movement against IT overcomplexity and  bastardization of classic Unix