Changing Parts of the History Expansion
In some cases, a command from the history or part of a command from the history does not exactly match the desired action. Maybe a path component is wrong, maybe the suffix of a used file name is different, or maybe only a few characters need to be exchanged to get the desired result. This is where another history expansion mechanism comes into play. The parts selected via event identifier and word identifier can still be modified before use. To do this, another colon “:” can be added to the previous identifier, followed by a so-called modifier. This modifier specifies how the selected part of the history should be modified before it is used.
We start with the following history:
[user01@aixe01 ~]$ history
1 cksum /opt/freeware/lib/gcc/powerpc-ibm-aix188.8.131.52/8/libstdc++.a
2 sudo stopsrc -s nimsh
In command number 1, a file was accessed via a relatively long absolute path. Let’s assume that a few commands later, the permissions for the directory of this file are of interest. The last argument of the command can be selected with the identifiers “!1:$” or “!cks:$” or “!?std?:$“. We want to run the “ls –ld” command on the associated directory. I.e. the file name at the end of the argument interferes here and is in excess.
The filename can be removed by the modifier “h” (head). For paths, this removes the last path component:
[user01@aixe01 ~]$ ls -ld !cks:$:h
ls -ld /opt/freeware/lib/gcc/powerpc-ibm-aix184.108.40.206/8
drwxr-xr-x 4 root system 256 Mar 18 07:46 /opt/freeware/lib/gcc/powerpc-ibm-aix220.127.116.11/8/
If we made a mistake when specifying the destination for an ls command, then that’s no big deal. The file(s) or directory(s) are only displayed and not changed or even deleted. If, to stay with the example above, you want to remove the directory including its contents “rm -rf“, then it is of course unfavorable if the history expansion (including the changes) does not lead to the expected argument. The wrong directory would then be deleted, with potentially fatal consequences.
So it would be nice if you could first look at the result of the replacement without executing the resulting command right away. This is exactly what the “p” (print, but don’t execute) modifier allows. It can be appended with another colon “:“:
[user01@aixe01 ~]$ rm -rf !cks:$:h:p
rm -rf /opt/freeware/lib/gcc/powerpc-ibm-aix18.104.22.168/8
If the displayed command is the desired command, it can be selected by pressing the cursor-up key, or executed by using “!!“.
As the next example, let’s look at command number 2 (“sudo stopsrc -s nimsh“). A service (NIM Service Handler) was stopped here. A service is then started again relatively often a short time later. The associated command would then be “sudo startsrc –s nimsh”. If you replace the string “op” with “art” in command 2, you get the correct start command for the service. The modifier “s” (substitute) is used for character replacements of this kind, which is used in the same way as you know it from the sed command under UNIX: “s/old/new/”. This makes it easy to start the service again. You can use “!2” or “!sudo” or “!?stop?” or “!?nimsh?” to select the stop command, and then add the modifier “:s/op/art/” to the start command:
[user01@aixe01 ~]$ !?nimsh?:s/op/art/
sudo startsrc -s nimsh
0513-059 The nimsh Subsystem has been started. Subsystem PID is 3998112.
Let’s assume we want to stop the service immediately again. The start command was now the last command. So we can refer to the stop command with “!!“. This time “art” must be replaced by “op“. That would be a total of: “!!:s/art/op/“. Since it is relatively common for the last command to be corrected, there is a separate abbreviation for this. Namely “^old^new^“. The last character (“^“) can be omitted if followed by a newline. This allows you to write just “^art^op”:
[user01@aixe01 ~]$ ^art^op
sudo stopsrc -s nimsh
0513-044 The nimsh Subsystem was requested to stop.
This is much easier than using the cursor-up button to retrieve the command and then move the cursor to the “art” position and then exchanging the characters manually!
The replacement is done only once! If you want to replace all occurrences, you can precede the modifier “s” with the letter “g” (global).
Let’s say the command “echo hello hello hello” was run recently. If you start the command again by using “:s” to replace the character string “hello” with “hi“, only the first occurrence is replaced:
[user01@aixe01 ~]$ !echo:s/hello/hi/
echo hi hello hello
hi hello hello
On the other hand, if you use “:gs” instead of “:s“, all three occurrences of “hello” are replaced by “hi“:
[user01@aixe01 ~]$ !echo:gs/hello/hi/
echo hi hi hi
hi hi hi
Below is an overview of all modifiers (including those not mentioned above):
- h Remove the last path component (head)
- t Remove all path components except the last one (tail)
- r Removing a suffix of the form .xxx
- e Remove everything but the suffix.
- p Output the command, but do not execute it.
- q Quoting of the replaced words. All replaced words are put together in a pair of apostrophes.
- x Quoting of the replaced words. Each replaced word is wrapped by their own pair of apostrophes.
- s/old/new The first occurrence of “old” is replaced with “new“.
- & The last replacement is repeated.
- g The replacement is performed globally, every occurrence is replaced.
- G The replacement is performed only once for each word.