1

I need to ask you about using grep command in a Bash script in Debian.

I have got for example file with these lines:

/fruit-/apple.txt
/fruit-/banana.txt
/fruit-/samples
/vegetables-/carrot.txt
/vegetables-/garlic.txt

I want to select all lines where is word fruit-.

I can call command:

grep -w "fruit-" file.txt

Output will be:

/fruit-/apple.txt
/fruit-/banana.txt
/fruit-/samples

But when I use command:

grep -w "fruit" file.txt

I also get same output such as above. But it's wrong. Output should be 0. Because I don't type - in pattern.

Why doesn't grep treat the - correctly?

0

2 Answers 2

6

The -w option indeed tells grep to only look for lines that match fruit as a "word", meaning that it must either start at the beginning of the line or be preceded by a "non-word" character, and either end at the end of the line or be followed by a "non-word" character.

However, a "word" character as per the man-page of grep is:

Word-constituent characters are letters, digits, and the underscore.

That means that the - is a "non-word" character, and fruit- will match the "word-search" for fruit as the matching algorithm will stop upon reaching the -.

Now, it seems you want to select only those lines where the content between the first two / is exactly fruit, as opposed to containing the pattern fruit. In these cases, you have to make the match more explicit:

  • With grep, you can say:
    grep "^/fruit/" file.txt
    
    This will anchor the pattern to the beginning of the line and only accept those lines where there is no - after the fruit.
  • Alternatively, use awk with the / set as field-separator:
    awk -F/ '!$1&&$2=="fruit"' file.txt
    
    This will only accept lines which have an empty first field (i.e. start right with a /) and whose second field is exactly fruit.
0
2

@AdminBee has already clarified what a word meant for grep, I'll just add how you can tell it to use other definition of word in this context.

grep -w word is more or less looking for words that are neither preceded nor followed by word characters.

Some grep implementations have a -P option to switch to perl-like regexps.

perl has explicit look around operators intended for checking whether something matches before or after.

grep -w word would be something grep -P '(?<!\w)word(?!\w)' where (?<!pattern) and (?!pattern) are respectively negative look behind and ahead operators.

If you want to consider that - is a word constituent, you can change it to:

grep -P '(?<![\w-])fruit(?![\w-])'

Which will match on foo fruit bar or foo/fruit/bar or foo/fruit but not on foo/fruit-bar as fruit is followed by a -.

Or for any character but /:

grep -P '(?<![^/])fruit(?![^/])'

Or for whitespace delimited words:

grep -P '(?<!\S)word(?!\S)'

(where \S, same as [^\s] similar to POSIX [^[:space:]] is any character not classified as whitespace).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .