I want to know how to use
grep in order to display all lines that begin and end with the same character.
pattern='\(.\).*\1 .' grep -x -- "$pattern" file
It won't work if line starts or ends with invalid byte character, if you want to cover that case, you can add
LC_ALL=C works with single-byte character data only.
perl6 seems to be the best tool, if you have it in your box:
$ printf '\ue7\u301 blah \u107\u327\n121\n1\n123\n' | perl6 -ne '.say if m/^(.).*$0$/ || /^.$/' ḉ blah ḉ 121 1
Although it still chokes on invalid characters.
perl6 will alter your text by turning it to
$ printf '\u0044\u0323\u0307\n' | perl6 -pe '' | perl -CI -ne 'printf "U+%04x\n", ord for split //' U+1e0c U+0307 U+000a $ printf '\u0044\u0323\u0307\n' | perl -pe '' | perl -CI -ne 'printf "U+%04x\n", ord for split //' U+0044 U+0323 U+0307 U+000a
perl6 stores string in
NFG form (stand for
Normalization Form Grapheme), which is
perl6 invented way to deal with un-precomposed graphemes properly:
$ printf '\u0044\u0323\u0307\n' | perl6 -ne '.chars.say' 1 $ printf '\u0044\u0323\u0307\n' | perl6 -ne '.codes.say' 2
Not grep but awk:
awk -F "" 'NF && $1 == $NF'
These special cases are handled:
- it does not print empty lines
- it always prints 1-character lines
An empty FS splits the record into one character per field in
awk (bytes, not characters for the latter two), but is not standard and doesn't work in the implementations of
awk derived from the original one by A, W and K like on BSDs and commercial Unices. More portable but more to type:
awk '/./ && substr($0,1,1) == substr($0,length)'
grep -xe '\(.\).*\1' -e .
$ printf '%s\n' il y était cet été | grep -xe '\(.\).*\1' -e . y été
-x is for exact match (match on the whole line).
\1 being a back-reference to the character captured in
\(.\). We add a
-e . to take care of the special case of a line containing one single character.
It assumes the input contains valid text in the current locale.
The match is on character, not byte (those é in UTF-8 are the two bytes 0xc3 0xa9 for instance), nor graphem cluster (it wouldn't work if those é were written in their decomposed form with
e followed by the U+0301 combining acute accent for instance).
To work on graphem clusters, with a
grep that supports
-P for PCRE:
$ printf 'e\u0301te\u0301\n' | grep -xPe '(\X).*\1|\X' été
That assumes the decomposition is the same for the two clusters, for instance a
ḉ expressed as
U+0327 would not match one expressed as
U+0301 or ḉ (
U+1E09). For that, you'd need to do the check on a normalized form:
$ printf '\ue7\u301 blah \u107\u327\n' | perl -MUnicode::Normalize -C -ne ' print if /^\X$/ || NFC($_) =~ /^(\X).*\1$/' ḉ blah ḉ
Quick python2 alternative:
python -c 'import sys;[sys.stdout.write(l) for l in sys.stdin if len(l)>1 and l.rstrip("\n").endswith(l)]' < input.txt
$ python -c 'import sys;[sys.stdout.write(l) for l in sys.stdin if len(l)>1 and l.rstrip("\n").endswith(l)]' < input.txt | cat -A nathan$ ookie $ a line a$