bash
was initially designed in the late 80s as a partial clone of ksh
with some interactive features from csh/tcsh.
The origins of globbing have to be found in those earlier shells which it builds upon.
ksh
itself is an extension of the Bourne shell. The Bourne shell itself (first released in 1979 in Unix V7) was a clean implementation from scratch, but it did not depart completely from the Thompson shell (the shell of V1 -> V6) and incorporated features from the Mashey shell.
In particular, command arguments were still separated by blanks, |
was now the new pipe operator but ^
was still supported as an alternative (and also explains why you do [!a-z]
and not [^a-z]
), $1
was still the first argument to a script and backslash was still the escape character. So many of the regexp operators (^\|$
) have a special meaning of their own in the shell.
The Thompson shell relied on an external utility for globbing. When sh
found unquoted *
, [
or ?
s in the command, it would run the command through glob
.
rm *.txt
would end up running glob as:
["glob", "rm", "*.txt"]
and glob would end up running rm
with the list of files matching that pattern.
grep a.\*b *.txt
would run glob
as:
["glob", "grep", "a.\252b", "*.txt"]
The *
above has been quoted by setting the 8th bit on that character, preventing glob
from treating it as a wildcard. glob
would then remove that bit before calling grep
.
To do the equivalent with regexps, that would have been:
regexp rm '\.txt$'
Or:
regexp rm '^[^.].*\.txt$'
to exclude dot-files.
The need to escape the operators as they double as shell special characters, the fact that .
, common in filenames is a regexp operator makes it not very appropriate to match filenames and complicated for a beginner. In most cases, all you need is wildcards that can replace either one (?
) or any number (*
) of characters.
Now, different shells added different globbing operators. Nowadays, the ksh and zsh globs (and to some extent bash -O extglob
which implements a subset of ksh globs) are functionally equivalent to regexps with a syntax that is less cumbersome to use with filenames and the current shell syntax. For instance, in zsh
(with extendedglob extension), you can do:
echo a#.txt
if you want (unlikely) to match filenames that consist of sequences of a
followed by .txt
. Easier than echo (^a*\.txt$)
(here using braces as a way to isolate the regex operators from the shell operators which could have been one way shells could deal with it).
echo (foo|bar|<1-20>).(#i)mpg
For mpg files (case insensitive) whose basename is foo, bar or a decimal number from 1 to 20...
ksh93
now can also incorporate regexps (basic, extended, perl-like or "augmented") in its globs (though it's quite buggy) and even provides a tool to convert between glob and regexp (printf %R
, printf %P
):
echo ~(Ei:.*\.txt)
to match (non-hidden) txt files with Extended regular expressions, case-insensitively.
Your Ctrl-r is being intercepted by the kernel-based terminal cookied line processing engine.
While sleep
is running, the terminal is in cooked mode, which means that the kernel-based tty line editor is working. The tty line editor supports rudimentary command line editing. The erase key (usually set to Ctrl-h (backspace) or Del) and the kill key (usually Ctrl-U) are the best known special editing keys that can be used in this mode. This line editor is useful: it's what lets interactive utilities that use neither readline nor curses to read complete lines of input from the terminal while allowing the user to make typing corrections.
But there's another special key that's active in this mode. You can see it along with the other key settings in the output of stty -a
under the name rprnt
and its default setting is... you guessed it... Ctrl-r. The function of this key is to repaint the current command line, in case it has become corrupted or misaligned due to other terminal output.
To avoid this, you can disable the function with stty rprnt undef
.
Personally I am used to Ctrl-r being interpreted as a repaint command and I am surprised every time I try to do that in bash
and it does something different!
Best Answer
The short answer is that you can not use regular expressions to search the shell history. According to POSIX (the standard for Unix-like operating systems), you should be able to search using regular shell pattern matching (as used for filename globbing and with
case
statements). This feature is referred to as non-incremental search but it currently does not seem to be correctly implemented in Bash.POSIX specification
The POSIX specification for shell Command Line Editing (vi-mode) states that these search patterns should use regular shell pattern matching. While the
^
meta-character is used to match the start of a line, they are not regular expressions.Documented Bash implementation
Bash uses the GNU Readline library to provide its interactive line-editing and history searching capabilities. The official documentation for the Readline library focuses more on Emacs mode, but a short section in its manual, Readline vi Mode states that
Actual Bash implementation
After a number of experiments on two different systems, I found that the non-incremental searching in Bash/Readline does not work as described in its official documentation. I found that the
*
was treated as a literal asterisk rather than a pattern that matches multiple characters. Likewise, the?
and[
are also treated as literal characters.For comparison, I tried using Vi-mode in
tcsh
and verified that it correctly implements history searching as specified in the POSIX standard.I then downloaded and searched through the code for the Readline library and found its history searching functions use a simple substring search and don’t use any search pattern meta-characters – aside from the caret,
^
(see search.c from the git repository for the Readline library).I presume the Bash/Readline developers have yet to implement this feature. I couldn’t find a bug-list but the
CHANGES
files shows that they’ve been regularly fixing issues relating to Vi-mode.Update: This feature was implemented in Readline 8.0 (released with Bash 5.0 in January 2019). As documented in its CHANGES: