2

I have a file that has this pattern in Linux and Unix environments.

# file1
text1   98432230
text2    123412
text3        10
line4         0
line5         0
line6     40000
...         ...
line10       20
...         ... 

I am trying to exclude lines that end with 0, but not exclude lines ending with 10, 20, 40000, 98432230 and so on.

I have tested ,|grep -v "\0$", | grep -v "[[:space:]]0$", | grep -v " 0", | sed '/0/d', | sed "/0$/d" but none of them work as they exclude any line that ends in 0, including 10, 20, 40000 and 98432230.

Any suggestions?

3
  • 4
    Are you sure grep -v "[[:space:]]0$" and | grep -v " 0" didn't work? The second would fail if your file is tab-separated, but the first should work perfectly.
    – terdon
    Jul 21, 2022 at 15:05
  • 1
    Try grep -v "[[:space:]]0$" again, that ought to have worked.
    – Kusalananda
    Jul 21, 2022 at 17:03
  • 1
    Try single quotes (apostrophes) instead of double quotes on the grep -v patterns that include the dollar sign. That is, grep -v '[[:space:]]0$' should work. The shell will try to expand the $ inside double quotes and it won't be passed to the grep.
    – mpez0
    Jul 22, 2022 at 15:48

4 Answers 4

15

Don't use grep, do it in awk instead:

$ awk '$NF!=0' file
text1   98432230
text2    123412
text3        10
line6     40000
...         ...
line10       20
...         ... 

In awk, the variable NF s the number of fields, so $NF is the last field. Expressions that evaluate to true mean "print this line" so $NF!=0 means "print every line whose last field is not 0".

You could even simplify it further to just:

awk '$NF' file
4
  • Thanks goof alternative, I rarely use awk so didn't trie that.
    – Intnox
    Jul 21, 2022 at 16:03
  • Or $NF != "0", if one wants to only exclude lines where the last field consists of that one digit 0 exactly. The default behaviour of awk would be to compare numerically, meaning that $NF != 0 would also exclude 000, 0.0, and 0e42. Probably not a problem, perhaps even what one would want, but it's useful to be aware of that.
    – ilkkachu
    Jul 22, 2022 at 9:54
  • 1
    @ilkkachu yes, that's precisely why I used != 0, so that 0.0 and 000 etc would also be removed.
    – terdon
    Jul 22, 2022 at 9:58
  • This question (and answers) should be duplicated in codeGolf ;)
    – dna
    Jul 22, 2022 at 12:21
4

For grep, you want a regular expression that matches three things: a character that is not numeric, followed by a '0', at the end of the line:

grep -v '[^0-9]0$'

Will you want to also match and discard any lines that end in just two zeroes, 00?

2
  • 5
    Not mentioned in the question, but this will also filter out 12.0 or similar values.
    – terdon
    Jul 21, 2022 at 15:37
  • Thank you, it works, I was thinking I had already tried that one.
    – Intnox
    Jul 21, 2022 at 16:00
3

You need to match a single 0, so you can say it's a whole "word"

grep -vw '0$' will only match (and exclude because of -v) lines that end with a "word" 0.

2
  • 2
    With the caveat that it'd also drop lines that end in 1.0. They don't have any in this question here, but it's a reasonable thing someone might have.
    – ilkkachu
    Jul 22, 2022 at 9:46
  • And it will not drop lines ending with something like "a0" as that would be a single word as well.
    – aragaer
    Jul 22, 2022 at 14:05
3

Using Raku (formerly known as Perl_6)

raku -ne '.put if .grep( none / \D 0 $/ );'    

Above uses Raku's grep routine. The regex atom \D is a non-digit. This routine takes advantage of Raku's none junction, which tests for the absence of the regex match. If you just put the return without the if conditional, you get the original file back with blank lines where no matches are found.

Sample Input:

text1  98432230
text2    123412
text3        10
line4         0
line5         0
line6     40000
line10       20

Sample Output:

text1  98432230
text2    123412
text3        10
line6     40000
line10       20

TMTOWTDI: In this case swapping out grep and using match instead gives the same return. Also match(/…/) can be written more simply as m/…/, and none inside the grep/match operator can be swapped with ! preceeding, as follows. (Special thanks to Bruce Gray for insights/discussion):

raku -ne '.put if .match( none / \D 0 $/ );' 

#OR

raku -ne '.put if  ! .match( / \D 0 $/ );'  

#OR

raku -ne '.put if  ! m/ \D 0 $/;'  

https://docs.raku.org/routine/none
https://docs.raku.org/routine/grep
https://raku.org

You must log in to answer this question.

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