2

How can I match a unix line ending with grep? I already have a working script that uses unix2dos and cmp, but it's a bit slow, and a single grep command would fit in a lot better with the rest of my bash code.

I tried using a negative lookbehind on '\r'.

$ printf "foo\r\n" | grep -PUa '(?<!'$'\r'')$'
foo

Why doesn't that work? For the record, the regex pattern seems to evaluate just well this way:

$ printf '(?<!'$'\r'')$' | od -a
0000000   (   ?   <   !  cr   )   $
0000007

Update:

$ grep --version
grep (GNU grep) 2.24

on MINGW64 on windows 7.

7
  • If you need to match a newline not preceded with \r, just use '(?<!\r)\n' Commented Jan 19, 2017 at 10:10
  • printf "foo\n" | grep -PUa '(?<!\r)\n' shows no match
    – chtenb
    Commented Jan 19, 2017 at 10:13
  • Why doesn't that work? Works for me with grep (GNU grep) 2.25
    – Leon
    Commented Jan 19, 2017 at 10:18
  • @Leon so it shows no match for you, while $ printf "foo\n" | grep -PUa '(?<!'$'\r'')$' does?
    – chtenb
    Commented Jan 19, 2017 at 10:23
  • Yes, exactly (some silly text enforced by the limit on a shortest comment length)
    – Leon
    Commented Jan 19, 2017 at 10:25

1 Answer 1

3

Your solution with grep -PUa '(?<!'$'\r'')$' worked with a more recent version of grep (2.25). However the support for Perl-compatible regular expression (-P) is stated to be highly experimental even in that newer version of grep, so it's not surprising that it didn't work in the previous version.

Use the following basic regular expression: \([^\r]\|^\)$, i.e. the following grep command when running from bash:

grep -Ua '\([^'$'\r'']\|^\)$'

An example demonstrating that it correctly handles both empty and non-empty lines:

$ printf "foo\nbar\r\n\nx\n\r\ny\nbaz\n" | grep -Ua '\([^'$'\r'']\|^\)$'
foo

x
y
baz
$

EDIT

The solution above treats the last line not including an end-of-line symbol as if it ended with a unix line ending. E.g.

$ printf "foo\nbar" | grep -Ua '\([^'$'\r'']\|^\)$'
foo
bar

That can be fixed by appending an artificial CRLF to the input - if the input ends with a newline, then the extra (empty) line will be dropped by grep, otherwise it will make grep to drop the last line:

$ { printf "foo\nbar"; printf "\r\n"; } | grep -Ua '\([^'$'\r'']\|^\)$'
foo
$
1
  • It doesn't work when there is text but no line endings: it will still match, even when there are no unix line endings
    – chtenb
    Commented Jan 19, 2017 at 14:20

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