8

I need to find if any lines in a file begin with ** .

I cannot figure out how to do it because * is interpreted as a wildcard by the shell.

grep -i "^2" test.out

works if the line begins with a 2 but

grep -i "^**" test.out 

obviously doesn't work.

(I also need to know if this line ends with a ) but have not attempted that yet).

18

Use the \ character to escape the * to make it a normal character.

grep '^\*\*' test.out

Also note the single quote ' and not double quote " to prevent the shell expanding things

  • 3
    The first * doesn't need escaping in this particular case, fwiw. – don_crissti Jun 23 '16 at 18:28
  • 5
    Although it doesn't have to be escaped, not escaping the first asterisk will be more confusing from a casual observer, as if you're matching "any number of starts of lines followed by an asterisk". Anything that makes a regexp more intuitive to a maintainer is a good idea. =) – Conspicuous Compiler Jun 23 '16 at 19:22
  • 1
    Using double quotes would also prevent expansion, not only single quotes. Wrong advice here. – rems4e Jun 24 '16 at 6:39
  • @rems4e using double quotes would prevent the shell from expanding the asterisks, but not from interpreting the backslashes. With double-quotes, you'd have to use grep "^\\*\\*" so that grep receives the ^\*\* string that it needs to avoid interpreting the asterisks as regex quantifiers. – Aaron Jun 24 '16 at 14:29
7

As you wanted to check the line which starts with ** and ends with ), you can combine two grep operation like this,

grep '^*\*' test.out | grep ')$'

Or with single grep command like this,

grep -E '^\*\*.*\)$' test.out

Explanation

  • ^\*\* : match line which starts with **
  • .* : match everything after **
  • \)$ : match line which also has ) at the end of line.
3

It's not the shell

None of the answers so far has touched on the real problem. It would be helpful to explain why it does not work as you expect.

grep -i "^**" test.out

Because you have quoted the pattern to grep, * is not expanded by the shell. It is passed to grep as-is. This is explained in the manual page[1] for bash[2]:

Enclosing characters in double quotes preserves the literal value of all characters within the quotes, with the exception of $, `, \, and, when history expansion is enabled, !.

It's regular ordinary regular expressions

A regular expression is a pattern that describes a set of strings.

* is one of the key patterns in regular expressions. By default, grep interprets it as follows:

* The preceding item will be matched zero or more times.

This means that your pattern as it stands, ^** does not make much sense. Possibly it tries to match the beginning of the line zero or more times, twice. Whatever that means.

The solution is to quote it:

Any meta-character with special meaning may be quoted by preceding it with a backslash.

grep -i "^\*\*" test.out


[1] I do not recommend reading it. Please use man dash or similar instead.

[2] No shell was given, so I assume bash.

2

Other options.

You can use sed or awk also

$ sed -n '/^*\*/p' test.out
$ awk '/^*\*/' test.out

To know lines that end with ) use also grep or sed or awk

$ grep ')$' test.out
$ sed -n '/)$/p' test.out
$ awk '/)$/' test.out
  • Thanks everyone ! I really appreciate the help on this one! – Shar Hunter Jun 23 '16 at 19:00
  • 1
    And the combined awk line: '/^*\*/&&/)$/'... – jasonwryan Jun 23 '16 at 19:10
0

This is the completely unquoted version: grep ^\\*\\* test.out. To pass a literal backslash from the shell to grep, it needs to be escaped.

This works as long as you have no files in the directory starting with ^\ and containing another backslash.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.