|
Softpanorama
(slightly skeptical)
Open Source Software Educational Society |
May the
source be with you,
but remember the KISS principle ;-)
|
Command History Reuse in Shell
"Those who cannot remember the past are condemned
to repeat it."
George Santayana (1863-1952)
|
All modern shell have mechanism of working with history of commands that was
originally introduced in C-shell. This mechanism that was created before current
terminals were available now looks outdated. And it is really very useful even in
the current days of powerful GUI and 3GHz/1G of memory/100G harddrive laptops. Famous
quote "Those who cannot remember the past are condemned to repeat it" has
pretty literal meaning here :-).
So learning it you need to know were to stop. Different people learn different
ways to work with shell so the set of features one adopts is usually quite individual.
For example I never learned to use internal search shell and always prefer to use
external via grep. In the same vain I never learned to use
pushd/popd -- those
crutches for a separate
cd history. For me there are just too many
features to learn in Unix shells and as I no longer work as a system administrator
on regular basis my skills periodically deteriorate to the extent that I read
this page with great interest :-). Then, shamed by my own ignorance, I relearn
some trick again and forget them in a couple of months. And this cycle looks like
an infinite loop. In this sense the famous quote "Those who cannot remember
the past are condemned to repeat it." has a different more menacing
meaning... Shell does suffer from
Creeping featurism:
1. Describes a systematic tendency to load more
chrome
and features
onto systems at the expense of whatever elegance they may have possessed when
originally designed. See also
feeping
creaturism. "You know, the main problem with
BSD
Unix has always been creeping featurism." 2. More generally, the tendency for
anything complicated to become even more complicated because people keep saying
"Gee, it would be even better if it had this feature too". (See
feature.)
The result is usually a patchwork because it grew one ad-hoc step at a time,
rather than being planned. Planning is a lot of work, but it's easy to add just
one extra little feature to help someone ... and then another ... and another....
When creeping featurism gets out of hand, it's like a cancer. Usually this term
is used to describe computer programs, but it could also be said of the federal
government, the IRS 1040 form, and new cars. A similar phenomenon sometimes
afflicts conscious redesigns; see
second-system
effect. See also
creeping
elegance.
Here is later definition from
Wikipedia:
Creeping featurism, or creeping featuritis,
is a phrase used to describe
software which over-emphasizes new
features to the detriment of other
design goals, such as
simplicity,
compactness,
stability, or
bug reduction. The term is often
spoonerized as feeping creaturism, a term intended
to bring the image of the encroaching features as small, seemingly
benign animals that creep into a program, making their trademark
noises and demanding their own space, soon taking over the entire
project.
Creeping featurism is often accompanied by the mistaken belief
that "one small feature" will add zero incremental cost to a
project, where cost can be money, time, effort, or energy. A
related term, feature creep, describes the tendency for
a software project's completion to be delayed by the temptation
to keep adding new features, without a specific goal.
Due to creeping featurism there some built-in limits in productivity achievable
with any old technology. And often the way to raise productivity is to switch to
a newer technology. If you are interested is using shell history you might be also
interested in orthodox file managers that represent
visual extension of the shell and as such greatly simplify the work of system administrator
and tremendously raise the productivity. For Linux the most popular orthodox file
manger is probably Midnight commander.
The Korn shell, POSIX shell and bash saves commands entered from your terminal
device to a history file. If set, the HISTFILE variable value is the name
of the history file. If the HISTFILE variable is not set or cannot be written,
the history file used is $HOME/.sh_history. If the history file does not
exist and the Korn shell cannot create it, or if it does exist and the Korn shell
does not have permission to append to it, then the Korn shell uses a temporary file
as the history file. The shell accesses the commands of all interactive shells using
the same named history file with appropriate permissions.
By default, the Korn shell or POSIX shell saves the text of the last 128 commands
entered from a terminal device. The history file size (specified by the HISTSIZE
variable) is not limited, although a very large history file can cause the Korn
shell to start up slowly. Korn shell traditionally uses the fc built-in command
to list or edit portions of the history file. It is usually aliased to history.
To select a portion of the file to edit or list, specify the number or the first
character or characters of the command. You can specify a single command or range
of commands.
Still at least some of the capabilities provided by history substitution mechanism
remains to be very useful although complex operations for modifying those commands
(that are too rarely needed to justify the learning curve) are simpler to do via
cut and paste. You need to activate in-line editing to be able to modify the command
line.
To activate in-line editing you must have the EDITOR variable set to vi
or emacs. You may use the set -o vi or set -o emacs command
to select an in-line edit mode. This informs the shell you want to be in in-line
edit mode on each command line.
Among many other innovations, the C Shell introduced innovative approach to using
history file that is now more or less completely replicated in bash. That is, you
type in a command and the shell locates the command line in the history file and
performs the editing commands you specified. Each command line in the history file
is referenced by an event number. For example, the following output is from the
history command:
- 1 vi antispam105.pl
- 2 perl -c antispam105.pl
- 3 cp antispam105.pl /usr/local/etc
- 4 find /spam/Spam_archive
-name "nobody" -print
- 5 vi apiprog.c
Event number 1 references the command line
"vi antispam105.pl" The command,
!3
reexecutes the "cp antispam105.pl /usr/local/etc"
command. There are two more useful shortcuts:
!string runs the most recent command beginning with the
string you specify.
- A !! refers to the last command in the history list.
You can modify the command on the fly using search-and-replace type modifier
s/old/new/ (or s^old^new^ -- the delimiter is actually
arbitrary) but I never managed to make a use of this feature.
More useful is the ability to specify the last argument of the prev command.
!!:$ designates the last word of the preceding command. This may be
shortened to !$. The addressing remings vi and as in vi you can
work with any command in history, not only the last.
Similarlity you can retrieve not only the last work but any word. For example,
!find:5 displays the fifth word from the last command beginning with
!find, while !find:$ would display the last word from
the same command. Please note that !find:* is same as !find:1-$
- both resulting in the complete command except for the first word. You type
new command with the same arguments that way.
Again I was never able to use this feature on regular basis and always slipped
to printing the whole command and then assembling new copying the relevant parts
with the mouse.
Entering the history command without any switches displays the full
history list with line numbers. If that's too much information, enter history
N where N is the number of previously executed commands you want displayed.
C-shell style incremental search of the history list is possible via
ctrl-r or ctrl-s (the latter performs a forward search).
You can grep last 100 or more commands in history for the commands you want,
for example:
history 100 | grep '^cd'
But it is better to create a shell a special function (with the name like hs)
to do that (this function can be made shell independent if all RC scripts for your
shells export the location of history file in the same variable for example HISTFILE.
If you use shells other then bash you may need explicitly export a variable that
points to your shell history in you RC file. For example:
export HISTFILE="$HOME/.history"
export HISTIGNORE="&:ls:ll:cd \-:ls -l:cd .."
export HISTCONTROL=erasedups
and a function like:
function hs {
grep $1 $HISTORY
}
Shell history issues became more complex if there are multiple administrators
on a particular server. The best way to solve this problem is to individualize shell
history files for root based on original id of the user.
Notes:
- This is a Spartan WHYFF (We Help
You For Free) site written by people for whom English
is not a native language.
Some amount of grammar and spelling errors should be
expected.
- The site contain some broken links
as it develops like a living tree...
Please try to use Google, Open directory,
etc. to find a replacement link (see
HOWTO search the WEB for details). We would appreciate
if you can
mail us a correct link.
|
|
|
|
O'Reilly ONLamp Blog
If you’re like me, you never want to lose a command. I’m
constantly searching back through them to find out just what
those command line flags were, what the esoteric command is
(and where it’s located), and most of all: what in Tcl’s
name did I do last month when I installed
foobazzulator. First thing to know: control-r.
However, bash ships with a ridiculously short memory: 500
commands. That’s not enough: right now, my powerbook’s iTerm
prompt is:
[/Users/jwellons]
111280$
Go into your .bash_profile now and add these
lines, before you read on:
HISTFILESIZE=1000000000
HISTSIZE=1000000
Unless you’re very precocious, that should square you away
for about 10 years (I’m at about 22 months since
enlightenment).
Once you develop control-r as a fast way to search
your command history, you’ll notice immediate stress relief,
and as I do everyday, wish you’d done this years before.
Now that you have commands piling up, you can do more
than just use them. You can analyze them too, for fun and
profit (since time = money)!
The basic starter is
cut -f1 -d" " .bash_history | sort | uniq -c | sort -nr |
head -n 30
In other words, just what do you spend so much time typing?
Here’s the money: Anything in at least the top ten, assuming
it is not from some massive one-time splurge, needs to be
aliased to a one or two letter shorthand.
My top command is v for vim,
which I’ve executed 9225 times. If those two extra letters,
which require me to switch hands, then use adjacent fingers
take an extra half-second each time: that’s well over an
hour wasted! And what a terribly drudgerous and unhealthy
hour it is too: mindlessly typing im 9225
times!
Even worse, who knows how desperately I needed to open
that file, and yet I needed to type superfluous characters,
probably messing them up on the first three tries.
Actually, that’s only my second highest command. cl
tops the list at 11116. You see, last time I did this
analysis, I noticed I was spending (yes, spent, like
lines of code, there is no glory in using a lot of them)
many commands first cd‘ing to a directory, then
listing the contents. Here’s cl:
# Compress the cd, ls -l series of commands.
alias lc="cl"
function cl () {
if [ $# = 0 ]; then
cd && ll
else
cd "$*" && ll
fi
}
There you have it, accompanied by its most common typo. Add
this to your bash profile now, and you’ll add several hours
to your life. Hurry! Don’t wait! It may not seem like much
now, but no one lies on her deathbed wishing she had spent
more time first changing directories, waiting for it to
return, then typing ls.
Since I know you’re dying to see the rest, here’s my
whole thirty, unaliased for readability:
11116 cl
9225 vim
7833 ll The alias of ll depends on
the OS I’m on, but for my Mac, it’s ls -AGlFT.
5145 cd
4858 clear
4563 rm
3950 alias to log into the master db server at my
old workplace
3740 lt version of ll that puts most
recently modified stuff at the bottom. Mandatory! Add
-tr to ll above
3435 mod a function that does lt, but
runs it through tail
3236 mv
2103 ls
1887 grep
1863 perl
1834 df
1767 mzscheme
1679 alias to log into my personal server
1580 g++
1544 cat
1186 scp
1080 find
988 man
925 echo
921 mkdir
907 sudo
888 history
841 alias to log into the dev server at my old
workplace
831 cp
763 gpg
758 locate
733 gzip
Depending on response, we may do another section with pie
charts, finding all the typo permutations for simple
commands (there’s a lot), and searching for common
sequences.
What I really want to see, though, is your top commands.
April, 2006 (samag.com) When used as an audit utility, shell history has serious
drawbacks. For example:
- Once a shell history file contains the maximum number of commands, old
commands are removed as new ones are entered.
- History files may be modified by a user who is trying to cover his tracks.
Timestamps are not available to determine when a command was executed.
- History files are typically stored in the user's home directories making
it difficult to process the information.
- It can be tricky to tell who executed certain commands if someone used
su to become another user, such as root.
09.27.2006 | Techtarget.com
The Bash shell is the default shell environment
in most Linux distributions, including all flavours of Red Hat. One default
feature of the Bash shell is to record a history of all the commands entered
by a user in a log file called .bash_history, found in the user's home directory.
The history in this file can be retrieved from the command line using the
more or cat binaries or by using the internal Bash
command, history. For many users, it is useful to retrieve the
previously executed commands, usually to save the effort of re-typing them.
So why might we want to limit or disable this Bash command history? Well,
among the commands that your users type are file names, command names, IP addresses,
passwords and a myriad of other data that are potentially valuable to an attacker
intent on subverting or compromising your host. Keeping a record of this data
means an attacker may only need to compromise an individual .bash_history file
rather than a more difficult source of data.
To limit the size and behaviour of the .bash_history file, you need to edit
the behavior of the shell in the /etc/profile file (the central version of the
.bash_profile file usually contained in users home directories). Add or change
the following three lines to the file:
export HISTSIZE=100
export HISTFILESIZE=100
unset HISTFILE
The first two lines set the length and size of the history stored in the
file. The last line unsets the HISTFILE variable that tells the Bash shell not
to save history when the user logs out of the interactive session. This means
an online user will only be able to see the history of the last 100 commands,
and that history will disappear after the user logs out of the host.
A further way to ensure the command history is removed is to include a command
in the .bash_logout file (other shells use the .logout file). The contents of
the .bash_logout file are executed when the user logs out. You can see a simple
.bash_logout file on the following lines:
# ~/.bash_logout
/bin/rm -f $HOME/.bash_history
clear
The rm command will remove the .bash_history file from the users home directory
when the user logs out. To add this to the file you can edit the .bash_logout
file contained in the /etc/skel directory (the contents of this directory are
copied to the home directories of all new users when they are created). Existing
users will need the .bash_logout files in their home directories adjusted to
add the command.
There is, however, one gotcha associated with this change, and that is that
if an attacker has compromised your host, then the history of their actions
might not be recorded. Of course, any talented attacker will use other means
(including this exact method and others like the deletion or editing of log
files) to remove that history.
Word designators are used to select desired words from the event. A
`:' separates the event specification from the word designator. It may
be omitted if the word designator begins with a `^', `$',
`*', `-', or `%'. Words are numbered
from the beginning of the line, with the first word being denoted by 0 (zero).
Words are inserted into the current line separated by single spaces.
For example,
!!
- designates the preceding command. When you type this, the preceding
command is repeated in toto.
!!:$
- designates the last argument of the preceding command. This may be shortened
to
!$.
!fi:2
- designates the second argument of the most recent command starting with
the letters
fi.
Here are the word designators:
0 (zero)
- The
0th word. For many applications, this is the command
word.
n
- The nth word.
^
- The first argument; that is, word 1.
$
- The last argument.
%
- The word matched by the most recent `?string?'
search.
x-y
- A range of words; `-y' abbreviates `0-y'.
*
- All of the words, except the
0th. This is a synonym for
`1-$'. It is not an error to use `*' if there
is just one word in the event; the empty string is returned in that case.
x*
- Abbreviates `x-$'
x-
- Abbreviates `x-$' like `x*',
but omits the last word.
If a word designator is supplied without an event specification, the previous
command is used as the event.
accept-line (Newline or Return)
- Accept the line regardless of where the cursor is. If this line is non-empty,
add it to the history list according to the setting of the
HISTCONTROL
and HISTIGNORE variables. If this line is a modified history
line, then restore the history line to its original state.
previous-history (C-p)
- Move `back' through the history list, fetching the previous command.
next-history (C-n)
- Move `forward' through the history list, fetching the next command.
beginning-of-history (M-<)
- Move to the first line in the history.
end-of-history (M->)
- Move to the end of the input history, i.e., the line currently being
entered.
reverse-search-history (C-r)
- Search backward starting at the current line and moving `up' through
the history as necessary. This is an incremental search.
forward-search-history (C-s)
- Search forward starting at the current line and moving `down' through
the the history as necessary. This is an incremental search.
non-incremental-reverse-search-history (M-p)
- Search backward starting at the current line and moving `up' through
the history as necessary using a non-incremental search for a string supplied
by the user.
non-incremental-forward-search-history (M-n)
- Search forward starting at the current line and moving `down' through
the the history as necessary using a non-incremental search for a string
supplied by the user.
history-search-forward ()
- Search forward through the history for the string of characters between
the start of the current line and the point. This is a non-incremental search.
By default, this command is unbound.
history-search-backward ()
- Search backward through the history for the string of characters between
the start of the current line and the point. This is a non-incremental search.
By default, this command is unbound.
yank-nth-arg (M-C-y)
- Insert the first argument to the previous command (usually the second
word on the previous line) at point. With an argument n, insert
the nth word from the previous command (the words in the previous
command begin with word 0). A negative argument inserts the nth
word from the end of the previous command.
yank-last-arg (M-. or M-_)
- Insert last argument to the previous command (the last word of the previous
history entry). With an argument, behave exactly like
yank-nth-arg.
Successive calls to yank-last-arg move back through the history
list, inserting the last argument of each line in turn.
Entering the
history command without
any switches displays the history list with line numbers. If that's too much
information, enter history N
where N is the number of previously executed commands you want displayed.
Bash also allows for incremental search
on the history list. Use ctrl+r
for a backward/reverse search or ctrl+s
to perform a forward search. As you type the first few letters of the command,
the last command from the history list that matches with your string would be
displayed.
Since you are at the command line, you
can club combine some popular commands and cook up interesting surprises. One
such example is to pass history
through grep
along with the first few letters of a command. For example, try this simple
hack, which displays all the commands beginning with the letters you supply:
history | grep -i first-few-letters-of-command
History expansion
History expansion serves two purposes, the first
is repeating a command from the history list. The second is selecting portions
from a command for substituting into the current command.
History expansions are implemented by the history
expansion character '!'. The line selected from the history list is called 'event'
and portions of that command that are selected are called 'words'. Depending
on your needs, you can either use 'word' or 'event designators'.
Event designators
Event designators, invoked by the history
expansion character are used for command substitution, that is, repeating a
command from the list. !
starts history substitution. It can be escaped with a backslash (\), since it
is a special character. In this example, history command 1008 -- a simple
ls -- is repeated:
!1008
ls
animate
Desktop
DOWNLOADS
evolution
songs
sonu_result.png
star trek
Another useful event designator is
!string, which
runs the most recent command beginning with the string you specify.
Word designators
Word designators are used to select specified
words from an event. A colon (:) is used to separate word designators from events.
Words in a command are numbered from the beginning of the line, with the first
word denoted by zero (0). Once selected, a word is inserted into the current
command separated by a single space.
Some common word designators are
n, which refers to
the nth word; ^
refers to the first word; $
refers to the last word and *
refers to the complete line entry except for the first word, which is the command.
For example,
!conv:5 displays the fifth
word from the last command beginning with !conv,
while !conv:$
would display the last word from the same command. Please note that
!conv:* is same as
!conv:1-$ -
both resulting in the complete command except for the first word.
Modifying selected words
There are a number of modifiers that help
you to describe how you want the selections to behave. Each modifier in a command
needs to be preceeded with a colon (:) and you can use more than one modifier
in a command. A very popular modifier is p
which results in the command being listed instead of being executed. For example:
!l:p
ls -l
There is also a search-and-replace type
modifier that you can use when you wish to replace some text with some other.
This modifier is s/old/new.
For example:
!!:s/-l/-a/
ls -a
[...]
Note: A !! is used to refer to the last command in the history list. The last command was ls -l and so the -l was replaced with -a and the new command, ls -a was executed.
Conclusion
After encountering a thread in a popular forum
board that just brushed on Bash and its event designators, I've since done enough
reading from the man pages and the web to officially declare that it is a very
cool feature which requires very little effort to get hooked on.
The usage of the bash-history feature could
be as fun as helpful, also it can save a lot of time if you are one of those
who use bash-terminals for everything (if you are not, what the hell are you
doing here with Linux Mr. Green ...just kidding Wink )
I'm not going to talk in extent about the usage of bash-history, I only want
to show you a couple of tips. If you want to learn more about it you can take
a look to the chapter 7 of the Bash Reference Manual (such as ramfree
"pointed" long time ago, should be under every linux user's pillow Wink )
Here are the tips:
$ !n --Will execute the line n of the history record.
$ !-n --Will execute the command n lines back.
$ !! --Will execute the last command. As !-1 or "up-arrow + return"
$ !string --Will execute the most recent command starting with the given
string. In my experience string is just the first characters of the first word
of the command, so don't use space characters.
$ !?string[?] --Will execute the most recent command containing the given
string. The last "?" may be omitted if the string is followed by a new line.
So if the string includes space characters or similar close it with "?". This
can be used to avoid the error that appears with !string when used with long
strings.
$ ^string1^string2 --String2 will substitute string1 on the last command.
Cool, isn't it? This is equivalent to !!:s/string1/string2/ which is cooler
Wink Any one feels a bit like in vi?
$HISTIGNORE or How-To
improve the history usage:
You might want to set up the shell variable $HISTIGNORE to avoid having consecutive
duplicate commands or unuseful commands appended to the history list. So you'll
get rid of the never ending story of hitting the up arrow trough simple and
repetitive commands.
Here is an example of usage:
export HISTIGNORE="&:l[sl]:[fb]g:exit"
Setting this way the $HISTIGNORE, the consecutive duplicated commands, the simple
calls to ls, ll, fg and bg commands without arguments, and calls to the exit
command will not be appended to the history list, so navigate though it will
be much easier.
# BASH HISTORY SEARCH AND REPLACE
^string1^string2^ Quick substitution. Repeat the last command, replacing string1 with
string2. Equivalent to ``!!:s/string1/string2/''
# MORE BASH COMMAND LINE TRICKERY (messing with the history)
make a directory then move into it:
mkdir cgi-bin; cd !#$
!#$ is shorthand for "the first word of this command". If I wanted to pick the third word
out of the previous command, that would be: !!:3 (don't forget there is a zeroth word).
# execute the most recent command the contains the following string:
!?string
# globally search replace on the previous line:
!!:gs/old/new/
# HEADS AND TAILS
ever wanted to copy a few files in the same directory, all of which have same
long prefix, like: cp /usr/local/etc/apache/file1.txt /usr/local/etc/apache/file2.txt
you can grap and reuse that prefix. It's named !#:1:h like:
cp /usr/local/etc/apache/file1.txt !#:1:h/file2.txt !#:1:h/file3.txt
# SAVING A COMMAND WITHOUT EXECUTING
While using bash, if you have typed a long command, and then realize you don't want to execute it yet, don't delete it. Simply
append a # to the beginning of the line, and then hit enter. Bash will not execute the command, but will store it in history so later
you can go back, remove the # from the front, and execute it.
# GOING FORWARD A BACK A WORD
META-F goes forward a word
META-B goes backward a word
# COMPARE A FILE WITH IT'S VARIOUS VERSIONS IN THE SAME DIRECTORY
diff file.*
# EXAMPLE of a basic command line script
% for f in `ls | grep -v ".sh"`; { mv ${f} ${f}.sh }
# searching the history
Cntrl-R starts a reverse incremental search
# Keyboard shortcuts:
CTRL-k: delete ('kill') from cursor position to the end of the line
* CTRL-u: delete from cursor position to the beginning of the line
* ALT-d: delete from cursor position to the end of the current 'word'
* CTRL-w: delete from cursor position to the beginning of the current 'word'
* CTRL-a: move cursor to the first character of the line
* CTRL-e: move cursor beyond the last character of the line
* ALT-a: move cursor to the first character of the current 'word'
* ALT-e: move cursor to the last character of the current 'word'
* CTRL-y: insert latest deleted 'word'
* ESC-_ or !$: repeats the last argument of the previous command.
Example: You created a directory with mkdir /usr/local/bin/peter/pan. Now you want to change into that directory with cd. Instead of typing the path again, you type cd ESC-_ or
# Diff 2 files with a common base:
diff {alpha,beta}site/config/Config.pl
gh - grep history
#!/bin/bash
#
# gh - grep history
#
# A simple script for the forgetful and lazy (I use it all the time).
#
# This script allows you to re-use bash history commands...
#
# A: ...if you can't remember the whole command or path...
# B: ...if you don't feel like pressing the "up" arrow for 2 minutes...
# C: ...if you are too forgetful or lazy to type "grep <search-string> ~/.bash_history"!
#
# -Mike Putnam http://www.theputnams.net
#
# Usage: gh <search-string>
#
grep $1 ~/.bash_history
exit 0
On Sat, 5 May 2001, Joost van der Lugt wrote:
> Hi all,
>
> I've been looking into this for a litle bit and am unable to find an
> answer. This is a feature AFAIK only implemented by SuSE:
>
> At a prompt type one or more characters then use Page Up/Page Down
> to scroll through all commands that started with these characters.
> So typing:
>
> cd
>
> would then give a 'scrollable' list of anything that started with cd
> simply hit enter when you reach the command you were looking for.
>
> I know Ctrl-r and am familiar with ! but they are not as nice, and
> really not the same.
> Would anybody have a clue as to how this could be implemented?
>
> I don't think it is shell dependent, so I don't think it's a bash
> option but I could very well be wrong.
> (Tried the dotfile generator, but don't see anything there)
>
> The option is _really_ addictive therefor my post:)
> Thanks,
>
> --
> Cheers,
>
> Joost van der Lugt
>
>
> --
> To UNSUBSCRIBE, email to debian-user-request@lists.debian.org
> with a subject of "unsubscribe". Trouble? Contact listmaster@lists.debian.org>
If you've ever used GNU/Linux,
chances are good that you've used bash. Some people hold the belief
that using a GUI is faster than using a CLI. These people have obviously
never seen someone who uses a shell proficiently. In this tutorial,
I hope to show you just a few of the amazing features bash provides
that will increase your productivity in the shell.
Bang Bang and history
Everyone knows about bash
history, right? You'd be surprised. Most modern distributions come with
bash history enabled and working. If you've never done so before, try
using the up and down arrow keys to scroll through your command history.
The up arrow will cycle through your command history from newest to
oldest, and the down arrow does, well, the opposite.
As luck would have it, different
terminals handle arrow keys differently, so the brilliant minds behind
bash came up with additional methods for accessing and making use of
the command history. We'll start with history. This
command simply gives you a numbered list of the commands you've entered
with the oldest command having the smallest number. Simple right?
Here's an example of
history output:
190 ps -axu | grep htt
191 /www/bin/apachectl start
192 vi /usr/local/lib/php.ini
193 cat /www/logs/error_log
194 ps -auxw | grep http
195 pwd
This brings us to bang-bang
or !!. !! tells bash "repeat the
last command I entered." But the magic doesn't stop there, if you order
now, you'll also receive !xyz. !xyz
will allow you to run the last command beginning with xyz that you typed.
Be sure to add enough to the abbreviation to make it unique or you could
run into problems, for instance: In the example above,
ps was ran twice, then pwd. If you typed
!p you'd get the output of pwd.
Typing !ps is just enough to be unique and will execute
the ps -auxw | grep http entry in history. By typing
just enough to make your history request unique, give you a much better
chance of hitting your targeted command.
:p isn't just an emoticon
If you need to be very sure
of the command you're targeting, :p can be a huge
help. !xyz:p will print the command that would be
executed rather than executing it. :p is also clever
enough to add the printed command to your history list as the last command
executed (even though it didn't execute it) so that, if you decide that
you like what was printed, a !! is all you need to
make it happen, cap'n.
Bash provides a couple of
methods for searching the command history. Both are useful in different
situations. The first method is to simply type history,
find the number of the command you want and then type
!N where "N" is the number of the command you'd like to execute.
(:p works here too.) The other method is a tad more
complex but also adds flexibilty. ^r (ctrl-r) followed
by whatever you type will search the command history for that string.
The bonus here is that you're able to edit the command line you've searched
for before you send it down the line. While the second method is more
powerful, when doing some redundant task, it's much easier to remember
!22 than it is to muck with ctrl-r type searches or
even the arrow keys.
Bang dollar-sign
!$
is the "end" of the previous command. Consider the following example:
We start by looking for a word in a file
grep -i joe /some/long/directory/structure/user-lists/list-15
if joe is in that userlist, we want to remove him from it. We can either
fire up vi with that long directory tree as the argument, or as simply
as
vi !$
Which bash expands to:
vi /some/long/directory/structure/user-lists/list-15
A word of caution:
!$ expands to the end word of the previous command.
What's a word? The bash man page calls a word "A sequence of characters
considered as a single unit by the shell." If you haven't changed anything,
chances are good that a word is a quoted string or a white-space delimited
group of characters. What is a white-space delimited group of characters
? It's a group of characters that are separated from other characters
by some form of white-space (which could be a tab, space, etc.) If you're
in doubt, :p works here too.
Another thing to keep in
mind when using !$ is that if the previous command
had no agruments, !$ will expand to the previous command
rather than the most recent argument. This can be handy if, for example,
you forget to type vi and you just type the filename. A simple
vi !$ and you're in.
Similar to
!$ is !*. !* is all of
the arguments to the previous command rather than just the last one.
As usual, this is useful in many situations. Here's a simple example:
vi cd /stuff
oops!
[exit vi, twice]
!*
Which bash expands to: cd /stuff
Circumflex hats
Have you ever typed a command,
hit return and a micro-second later realized that you made a typo? Seems
like I'm always typing mroe filename. Luckily, the
folks who wrote bash weren't the greatest typists either. In bash, you
can fix typos in the previous command with a circumflex (^)
or "hat." Consider the following:
vi /etc/X11/XF86config
oops!
^6c^6C
Which bash turns into:
vi /etc/X11/XF86Config
What happened there? The
name of the file that I was trying to edit was /etc/X11/XF86Config (note
the capital "C.") I typed a lower-case "c" and vi
saw my error as a request for a new file. Once I closed out of
vi I was able to fix my mistake with the following
formula: ^error^correction.
Hats needn't be only used
for errors... Let's say you have a few redundant commands that can't
be handled with a wildcard, hats will work great for you. For example:
dd if=kern.flp of=/dev/fd0
^kern^mfsroot
Which bash turns into:
dd if=mfsroot.flp of=/dev/fd0
A few handy movement commands
Sometimes a mistake is noticed
before the enter key is pressed. We've already talked about terminals
that don't translate cursor-keys properly, so how do you fix a mistake?
To make matters worse, sometimes the backspace key gets mapped to ^H
or even worse something like ^[[~. Now how do you fix your mistake before
hitting the enter key?
Once again, bash comes through
for us. Here are some of the movement keystrokes that I use most often:
- ^w erase word
- ^u erase from here to beginning
of the line (I use this ALL the time.)
- ^a move the cursor to the beginning
of the line
- ^e move the curor to the end of
the line
There are more of course,
but those are the ones you simply can't live without. For those who
don't know the ^N notation means ctrl+N, don't confuse it with hats
mentioned above.
tab-tab
One of my favorite features
of bash is tab-completion. Tab-completion works in a couple of ways,
it can complete filenames in the current directory or in your $PATH.
Like the !commands above, you just need to give bash
enough of the filename to make it unique and hit the tab key -- bash
will do the rest for you. Let's say you have a file in your home directory
called ransom.note, consider the following:
mor[tab] ran[tab]
Will expand to
more ransom.note
Let's say you also have
a file named random in your home directory. ran
above is no longer enough to be unique, but you're in luck. If you hit
tab twice, bash will print the list of matching files to the screen
so that you can see what you need to add to make your shortcut unique.
Aliases
Using aliases is sort of
like creating your own commands. You decide what you want to type
and what happens when you type that. Aliases can live in a few of
different places, ~/.bashrc ~/.bash_profile ~/.profile and ~/.aliases
are some, but not all. In fact, you're not really limited to keeping
them all in once place. Those different files behave differently based
upon what kind of shell you're running, but that's beyond the scope
of this document. For the purposes of this discussion, we'll settle
on ~/.bash_profile (used for login shells.)
In that file, usually at
the bottom, I assemble my aliases. Here's some examples:
alias
startx='startx 2>&1 | tee ~/.Xlog &'
alias ls='ls --color=auto'
alias mroe='more'
alias H='kill -HUP'
alias getxcvs='CVS_RSH=ssh; export CVS_RSH; cvs -d anoncvs@anoncvs.xfree86.org:/cvs
checkout xc'
The bottom one will probably
wrap, but it provides a great example of why aliases are great. A whole
string of commands has been reduced to something short and easy to remember.
I hope this tutorial has
been useful to you. The most difficult hurdle here is not the learning
curve, but simply becoming accustomed to using these built-ins. Just
like learning vi, once you get good with these, you'll be amazed you
ever lived without them.
This is just the tip of
the bash iceberg. If you enjoyed this, you might want to look around
the Net for more bash information, or even buy a book!
Don't miss
part 2!
Send comments to Sam Rowe,
deadman at deadman dot org.
A history substitution begins with a !, and may
occur anywhere on the command line. The power of history substitution is impressive.
There are too many ways of using it to explain here, but complete details can
be found in the csh man page. Some of the simpler forms are:
!! Refer to the previous command
!n Refer to command-line n
!-n Refer to the current command-line minus n.
!str Refer to the most recent command starting with str.
!?str Refer to the most recent command containing str.
You can also refer to individual words of a previous
command. If necessary, you can separate the event specification (above) from
the word specification.
!!:0 The first word of the previous command line
!!:n The nth argument of the previous command
!!:$ The last argument of the previous command
!!:* All of the arguments of the previous command
Further modification to the commands can be done
including editing particular words, but we won't go into further detail here.
Suffice it to say that there's probably a way to do whatever it is you are trying
to accomplish with history substitution.
Quick substitution can be done, if all you want
to do is change a single string from the previous command. For instance, if
you just sent mail to dela with "mail dela", and now want to send mail to kaser,
you can say
^dela^kaser^
and the string "dela" will be replaced with "kaser".
bash stores the commands you type and provides access to them through its
history mechanism. This makes it possible to repeat a previous command,
include portions of a previous command in the current command or re-execute
a previous command after fixing a typing error.
The list of all commands currently saved by the history mechanism is displayed
by executing the history command. The history commands
numbers the old commands it displays. Typing
!n
will re-execute the command numbered n.
If you would rather not lookup the number of a previous command, you can
type ! followed by the first characters of the command. For example,
!ls
will execute the last command that began with ls.
Another way to avoid looking up the number is to use control-p.
Pressing control-p will display your previous command, which you
can then edit. Repeatedly pressing control-p will go back to earlier
commands.
If the command you want to execute is similar to a previous command, or you
want to fix a typing mistake, you can control-p back to the command,
then edit it using control-b or the left arrow key to move the
cursor back a character, contol-f or the right arrow key to move
forward a character, control-a to move to the beginning of a line,
control-e to move to the end of a line, and control-d
to delete a character. Backspace and Delete work as
usual. Anything you type is inserted where the cursor is located.
Another useful function the history mechanism provides is the ability to
re-use the arguments of the previous command. The characters !*
occurring in a command are replaced by the arguments to the previous command.
Thus, one can first check the contents of a file by typing
less somelongfilename
and then print the file by typing
lp !*
You can avoid typing somelongfilename by just typing enough of it so that
the shell can recognize it, (for example ``somel'' for the filename ``somelongfilename'')
and pressing tab. bash will type the rest of the filename for
you. If there is another file of a similar name, such as ``somelongfile2'',
and you use tab to complete the name, bash will type the beginning of the two
filenames until it encounters a discrepancy. In this case, it would type ``somelongfile''.
Then it will beep and wait for you to finish typing in the filename of your
choice.
Finally, you can fix simple typing mistakes in a command by typing
^old-string^new-string
This causes the shell to re-execute the last command after first replacing the
first occurrence of old-string by new-string.
Word designators are used to select desired words from the event. A
`:' separates the event specification from the word designator. It may
be omitted if the word designator begins with a `^', `$',
`*', `-', or `%'. Words are numbered
from the beginning of the line, with the first word being denoted by 0 (zero).
Words are inserted into the current line separated by single spaces.
For example,
!!
- designates the preceding command. When you type this, the preceding
command is repeated in toto.
!!:$
- designates the last argument of the preceding command. This may be shortened
to
!$.
!fi:2
- designates the second argument of the most recent command starting with
the letters
fi.
Here are the word designators:
0 (zero)
- The
0th word. For many applications, this is the command
word.
n
- The nth word.
^
- The first argument; that is, word 1.
$
- The last argument.
%
- The word matched by the most recent `?string?'
search.
x-y
- A range of words; `-y' abbreviates `0-y'.
*
- All of the words, except the
0th. This is a synonym for
`1-$'. It is not an error to use `*' if there
is just one word in the event; the empty string is returned in that case.
x*
- Abbreviates `x-$'
x-
- Abbreviates `x-$' like `x*',
but omits the last word.
If a word designator is supplied without an event specification, the previous
command is used as the event.
accept-line (Newline or Return)
- Accept the line regardless of where the cursor is. If this line is non-empty,
add it to the history list according to the setting of the
HISTCONTROL
and HISTIGNORE variables. If this line is a modified history
line, then restore the history line to its original state.
previous-history (C-p)
- Move `back' through the history list, fetching the previous command.
next-history (C-n)
- Move `forward' through the history list, fetching the next command.
beginning-of-history (M-<)
- Move to the first line in the history.
end-of-history (M->)
- Move to the end of the input history, i.e., the line currently being
entered.
reverse-search-history (C-r)
- Search backward starting at the current line and moving `up' through
the history as necessary. This is an incremental search.
forward-search-history (C-s)
- Search forward starting at the current line and moving `down' through
the the history as necessary. This is an incremental search.
non-incremental-reverse-search-history (M-p)
- Search backward starting at the current line and moving `up' through
the history as necessary using a non-incremental search for a string supplied
by the user.
non-incremental-forward-search-history (M-n)
- Search forward starting at the current line and moving `down' through
the the history as necessary using a non-incremental search for a string
supplied by the user.
history-search-forward ()
- Search forward through the history for the string of characters between
the start of the current line and the point. This is a non-incremental search.
By default, this command is unbound.
history-search-backward ()
- Search backward through the history for the string of characters between
the start of the current line and the point. This is a non-incremental search.
By default, this command is unbound.
yank-nth-arg (M-C-y)
- Insert the first argument to the previous command (usually the second
word on the previous line) at point. With an argument n, insert
the nth word from the previous command (the words in the previous
command begin with word 0). A negative argument inserts the nth
word from the end of the previous command.
yank-last-arg (M-. or M-_)
- Insert last argument to the previous command (the last word of the previous
history entry). With an argument, behave exactly like
yank-nth-arg.
Successive calls to yank-last-arg move back through the history
list, inserting the last argument of each line in turn.
HISTCONTROL
HISTCONTROL
A value of 'ignorespace' means to not enter lines which begin with
a space or tab into the history list. A value of 'ignoredups' means
to not enter lines which match the last entered line. A value of 'ignoreboth'
combines the two options. Unset, or set to any other value than those above, means
to save all lines on the history list. The second and subsequent lines of a multi-line
compound command are not tested, and are added to the history regardless of the
value of HISTCONTROL.
If the list of values includes ignorespace, lines which begin with a space
character are not saved in the history list. A value of ignoredups causes lines
matching the previous history entry to not be saved. A value of ignoreboth is shorthand
for ignorespace and ignoredups. A value of erasedups causes all previous lines matching
the current line to be removed from the history list before that line is saved.
Any value not in the above list is ignored. If HISTCONTROL is unset, or does not
include a valid value, all lines read by the shell parser are saved on the history
list, subject to the value of HISTIGNORE. The second and subsequent lines of a multi-line
compound
command are not tested, and are added to the history regardless of the value of
HISTCONTROL.
In bash 3.0 HISTCONTROL can be set to `erasedups' option, which causes all lines
matching a line being added to be removed from the history list.
export HISTCONTROL=erasedups
HISTIGNORE
A colon-separated list of patterns used to decide which command lines should be
saved on the history list. Each pattern is anchored at the beginning of the line
and must match the complete line (no implicit '*' is appended). Each
pattern is tested against the line after the checks specified by HISTCONTROL
are applied. In addition to the normal shell pattern matching characters,
'&' matches the previous history line. '&' may be escaped using
a backslash; the backslash is removed before attempting a match. The second and
subsequent lines of a multi-line compound command are not tested, and are added
to the history regardless of the value of HISTIGNORE. HISTIGNORE
subsumes the function of HISTCONTROL. A pattern of '&'
is identical to ignoredups, and a pattern of '[ ]*' is
identical to ignorespace. Combining these two patterns, separating
them with a colon, provides the functionality of ignoreboth.
You might want also to set up the shell variable HISTIGNORE to avoid useless
command littering you history. Natural candidates are cd -, all variants of ls without
argument, ll, etc.
export HISTIGNORE="&:l[sl]:[fb]g:cd \-"
Setting this way the $HISTIGNORE, the consecutive duplicated commands, the simple
calls to ls, ll, fg and bg commands without arguments, and calls to the exit command
will not be appended to the history list, so navigate though it will be much easier.
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 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. If the word designator begins with $, %, ^, *, or - the
: is not needed. 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. 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:
| # |
The entire command line. |
| 0 |
The first word on the command line, argument 0, which is the command
itself. For example, !2:0 refers to the word "write" in the previous history
listing. |
| $ |
The last argument on the command line. For example, !4:$
refers to the word "-print". |
| % |
The word matched by the most recent ?s event designator. |
| ^ |
The first argument (second word) on the command line. For example,
!4:^ refers to "/usr/local" on the "find ..." command line. |
| n |
The nth (nth + 1 word) on the command line. |
| * |
All arguments on the command line. Does not include word 0. This
is the same as 1-$. |
| x-y |
A range of words starting at word x and continuing through
word y. |
| x* |
An abbreviated form of x-$, which refers to word x
through the last word. |
| x- |
The same as x* except the last word is not included. |
| -y |
Same as the range 0-y. |
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 (:).
| & |
Repeat the previous substitution(s/old/new/). |
| e |
Remove everything but the suffix. |
| g |
Globally apply the change to the first occurrence of a match in
each word. The g must precede other modifiers. |
| h |
Remove the last filename or directory name from the path. Same as
performing a dirname on the designated word. |
| p |
Print (display) the command but do not execute it. |
| q |
Quote the substituted words, preventing further substitutions. |
| r |
Remove a suffix of the form ".xxx," leaving the basename. |
| s/old/new/ |
Substitute the new string in place of old string. |
| t |
Remove all leading pathname components, leaving the basename. |
| x |
Same as q, except words are delimited by spaces, tabs, and new-lines. |
The following examples illustrate how to use the Event Designators, Word Designators,
and the Modifiers.
-
| history |
List out the history events. |
-
| !10 |
Execute history event number 10. |
-
| !{l};.c |
Search for first event beginning with l and add .c to the end
of the command line. |
-
| !vi |
Execute the last command that started with vi. |
| ^bni^bin |
Substitute "bni" in the previous command line to "bin." |
- !vi:s/1.c/2.c/
|
Substitute "1.c" to "2.c" on the last command that started with
vi. |
-
!! Repeat the previous command history
- !2 Repeat the second command more mbox
- !v Repeat the last command which began with a vi vi letter.to.fred
- !-2 Repeat the command one previous to the last lc
- !??s Repeat the last command with the pattern third letter s
- !! > file2 Produces a file consisting of the last command
- !! | more Repeat the last command piped through more history | more
- !5:0 file4 cat file4
- vi !5:2 vi file2
- vi !5$ vi file3
- more !5:2-$ more file2 file3
- !5^ file1 (this would produce an error since file1 is not executable).
Archiving Korn Shell History Files
Maybe you want to keep your shell history around for forensic analysis...
When used as an audit utility, shell history has serious drawbacks. For example:
- Once a shell history file contains the maximum number of commands, old
commands are removed as new ones are entered.
- History files may be modified by a user who is trying to cover his tracks.
Timestamps are not available to determine when a command was executed.
- History files are typically stored in the user's home directories making
it difficult to process the information.
- It can be tricky to tell who executed certain commands if someone used
su to become another user, such as root.
Copyright © 1996-2009 by Dr. Nikolai Bezroukov.
www.softpanorama.org was
created as a service to the UN Sustainable Development Networking Programme (SDNP)
in the author free time.
Submit
comments This document is an industrial compilation designed and created
exclusively for educational use and is placed under the copyright of the
Open Content License(OPL).
Site uses AdSense so you need to be aware of Google privacy policy. Original materials copyright belong to respective owners. Quotes are made
for educational purposes only in compliance with the fair use doctrine.
Disclaimer:
- The statements, views and opinions presented on
this web page are those of the author and are not endorsed by, nor do they necessarily
reflect, the opinions of the author present and former employers, SDNP or any other
organization the author may be associated with.
- We do not warrant the correctness of the information provided or its
fitness for any purpose
- In no way this site is associated with or endorse cybersquatters
using
the term "softpanorama" with other main or country domains (e.g. softpanorama.com) with
bad faith intent to profit from the goodwill belonging to
someone else.
Last modified:
December 12, 2007
|