If your shell supports it (zsh
, bash
, some implementations of ksh
), you could utilise process substitution
grep <pattern> <(tail -n5 yourfile.txt)
Where -n5 means get the five last lines.
Similarly,
grep <pattern> <(head -n5 yourfile.txt)
would search through the 5 first lines of yourfile.txt.
Explanation
Simply speaking, the substituted process pretends to be a file, which is what grep is expecting. One advantage with process substitution is that you can feed output from multiple commands as input for other commands, like diff
in this example.
diff -y <(brew leaves) <(brew list)
This gets rid of the pipe (|
) character, but each substitution is in fact creating a pipe1.
1Note that with ksh93
on Linux at least, |
does not use a pipe but a socket pair while process substitution does use a pipe (as it's not possible to open
a socket):
$ ksh93 -c 'readlink <(:)'
pipe:[620224]
$ ksh93 -c ': | readlink /proc/self/fd/0'
socket:[621301]
This one is easy:
grep -E "199[5-9]"
does the job. It is easy, because the intended number range matches a character code range. For a more complicated example, e.g., 1998-2003, you will have to split the range appropriately:
grep -E "199[8-9]|200[0-3]"
Best Answer
Within a bracketed expression,
[...]
, very few character are "special" (only a very small subset, like]
,-
and^
, and the three combinations[=
,[:
and[.
). When including]
in[...]
, the]
must come first (possibly after a^
). I opted to put the]
first and the[
last for symmetry.The only other thing to remember is that a single quoted string can not include a single quote, so we use double quotes around the expression. Since we use a double quoted string, the shell will poke around in it for things to expand. For this reason, we escape the
$
as\$
which will make the shell give a literal$
togrep
, and we escape!
as\!
too as it's a history expansion inbash
(only in interactivebash
shells though).Would you want to include a backslash in the set, you would have to escape it as
\\
so that the shell gives a single backslash togrep
. Also, if you want to include a backtick`
, it too must be escaped as\`
as it starts a command substitution otherwise.The command above would extract any line that contained at least one of the characters in the bracketed expression.
Using a single quoted string instead of a double quoted string, which gets around most of the annoyances with what characters the shell interprets:
Here, the only thing to remember, apart from the placing of the
]
, is that a single quoted string can not include a single quote, so instead we use a concatenation of three strings:'[]:/?#@!$&'
"'"
'()*+,;=%[]'
Another approach would be to use the POSIX character class
[[:punct:]]
. This matches a single character from the set!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
, which is a larger set than what's given in the question (it additionally contains"-.<>^_`{|}~
), but is all the "punctuation characters" that POSIX defines.