0

The contents of the file testing.txt are:

ls -a
cmake --verbose
verbose

I want to use grep to look through this file and find only the word beginning with "--" i.e. the word "--verbose"

However using the following patterns as an argument for grep does not work:

$ cat testing.txt | grep -- 
Usage: grep [OPTION]... PATTERN
   [FILE]... Try 'grep --help' for more information.

$ cat testing.txt | grep -
ls -a
cmake --verbose

$ cat testing.txt | grep '--v'
grep (GNU grep) 3.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Mike Haertel and others, see <http://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>.

$ cat testing.txt | grep ver
cmake --verbose
verbose

$ cat testing.txt | grep '-ver'
ls -a
  • grep thinks that all arguments beginning with a -- are options? How do you prevent this so that grep can search for a pattern (in a file) that begins with "--"?
  • the last attempt uses the pattern "-ver" so that grep does not think the pattern is an option, but then grep does not match the word "--verbose" in the file even though it contains the pattern "-ver". What causes this behavior?
  • grep -Po '\--\w+' should help you pick your words. – Rakesh Sharma Jun 20 at 10:56
3

The string -- is special for most utilities when it occurs on the command line. It signals the end of options to the command line argument parser. It is used in situations where you may want to pass a filename that starts with a dash, as in rm -- -f (to delete a file called -f in the current directory).

To use -- as a pattern with grep, tell the utility explicitly that it is a pattern:

grep -e --

The -e option to grep takes an option argument which is the pattern that you want grep to search with.

You could also use

grep -- --

Here, grep knows that the second -- is the pattern, because the first -- says it can't be an option.


Your last pipeline returns ls -a because that's a line in the file that does not include an r. The command grep -ver may also be written grep -v -e r, i.e., "extract all the lines that do not (-v) match r (-e r)".

  • Simple answer, was expecting a complicated regular expression so I am pleased with this straight forward solution. Thanks for also adding a use case for the string --, it's very useful not just to understand what something is but also when to use it – MyWrathAcademia Jun 19 at 20:58
  • can you clarify "The -e option to grep takes an option argument which is the pattern that you want grep to search with. ", do you mean to say "The -e option to grep takes an optional argument which is the pattern that you want grep to search with"? – MyWrathAcademia Jun 19 at 21:00
  • 1
    @MyWrathAcademia No, not optional. An "option argument" is an argument to an option. The option -e takes an argument, its option argument. – Kusalananda Jun 19 at 21:03
  • Oh I see, you mean an "option's argument" i.e. an argument that belongs to an option. As for the last pipe containing line, good analysis. It's very interesting because I did not consider that grep combined the short options -v and e, but how come r is not taken as an option? Is it because it comes after e so grep thinks that r is an argument to the option e even without a space separating them. Shouldn't Linux commands assume only a space separated string is an argument to an option, other wise we get human error as shown here? – MyWrathAcademia Jun 19 at 21:14
  • @MyWrathAcademia For single letter options, spaces are irrelevant. This is not a Linux thing but is true on all Unix systems. – Kusalananda Jun 19 at 21:20
1

To answer the question in the title which I interpret as meaning: report the whitespace-delimited words that do not start with an alphanumeric or underscore (the \w in some regexp engines), you could do with GNU grep provided it's built with PCRE support:

grep -Po '(?<!\S)[^\w\s]\S*'

That is a character other (^) than a word one (\w) or whitespace one (\s) provided it's not preceded ((?<!...)) by a non-whitespace (\S), and followed by any number (*) of non-whitespace (\S).

Which on your input returns:

-a
--verbose
  • The closest ERE equivalent is grep -Eo '(^|[[:space:]])[^[:space:][:alnum:]]+\<[^[:space:]]+' – glenn jackman Jun 19 at 23:28
0

The simplistic answer is to "consume" the end of options -- signal first and then give the pattern to search for:

grep -- --

as in:

$ echo $'ls -a\ncmake --verbose\nverbose' | grep -- --
cmake --verbose

One alternative is to signal that the string -- is actually a pattern with -e

$ echo $'ls -a\ncmake --verbose\nverbose' | grep -e --

And the least elegant solution is to build a regex that match (only) the --. That regex can not start with --, so, we have to use a workaround, either:

grep '[-][-]'     or    grep '[-]\{2\}'     or    grep -E '[-]{2}'
0

With GNU grep you an approach this problem by means of positive lookarounds as shown:

 $ grep -oP '(?:(?!\h)\W)+\w+' inp

To be read as, from where i stand in the string, i see a nonword follow a word. But, that nonword precludes any whitespace.

Output:

 -a
 --verbose

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.