Can I write a pattern in sed
that matches patterns like Aa, Bb, Cc, etc. (i.e., Given an uppercase letter, it should match the corresponding lowercase letter) without enumerating all possibilities?
-
2Can you give a sample of input/output...?– George VasiliouJan 25, 2017 at 15:41
3 Answers
With perl
, you can do:
$ echo 'fooÉébAar' | perl -Mopen=locale -pe 's/([[:upper:]])(??{lc$^N})/<$&>/g'
foo<Éé>b<Aa>r
That uses the (??{code})
special perl operator, where you can dynamically specify the regexp to match on. Here lc$^N
is the lowercase version of $^N
, the last capture group.
With GNU sed
, you could do:
$ echo 'fooÉébAar' | sed -Ee 's/./&\L&/g;s/([[:upper:]](.)\2.)/<<\1>>/g;s/(.)./\1/g'
foo<Éé>b<Aa>r
The idea is that we first append each character in the input with their lower case version (X
becomes Xx
, x
becomes xx
), so if we see a Xxx
after that (([[:upper:]](.)\2
: X
followed a repeated character), that means we've got an uppercase character followed by its lower case version.
Note that those would not work for characters in decomposed form. For instance for É
when expressed as E
followed by a combining acute accent. To work around that you could use perl
's \X
graphem cluster regexp operator instead:
$ printf 'E\u0301\u0302\u00e9\u0302 \u00c9e\u301 foo Ee\u301\n' |
perl -Mopen=locale -MUnicode::Normalize -pe '
s/((?=[[:upper:]])\X)(?{$c1 = $^N})(\X)(??{
NFD(lc$c1) eq NFD($^N) ? qr{} : qr{(?!)}})/<$&>/g'
<É̂é̂> <Éé> foo Eé
Above using canonical normalisation forms (NFD
) so that graphem clusters are always represented in the same way at the character level.
It would still fail to match on things like Fffi
where that ffi
(U+FB03) is a single (typographical ligature) character but that's probably just as well anyway.
If you are using Sed in combination with Bash or Zsh, you can just use a small meta-program, like that:
Code
>echo "AaBCAABbEE"| sed -E "s/`echo {A..Z}|sed -E 's/\w/&\L&/g;y/ /|/'`/%/g"
%BCAA%EE
This will effectively generate all the combinations for you, using shell brace expansion (and nested sed), like illustrated below:
>echo {A..Z}|sed -E 's/\w/&\L&/g;y/ /|/'
Aa|Bb|Cc|Dd|Ee|Ff|Gg|Hh|Ii|Jj|Kk|Ll|Mm|Nn|Oo|Pp|Qq|Rr|Ss|Tt|Uu|Vv|Ww|Xx|Yy|Zz
There are probably some pure-Sed ways of doing this too, e.g. by applying several substitutions in a row and/or using a hold space to search for these pairs one by one.
Combining bash and sed (in this example i use sed to match and delete the letters):
$ word="Hey , This is AaBbCc and also Dd!"
$ search="H";search2=${search,,};sed "s/[$search$search2]//g" <<<"$word"
ey , Tis is AaBbCc and also Dd!
$ search="A";search2=${search,,};sed "s/[$search$search2]//g" <<<"$word"
Hey , This is BbCc nd lso Dd!
$ search="D";search2=${search,,};sed "s/[$search$search2]//g" <<<"$word"
Hey , This is AaBbCc an also !
You just need to provide the search letter in uppercase at var "search=".
PS: If you need to search for lowercase letters then those can be transformed to uppercase with ${search^^}
-
Your pattern patches
either
the upper or lower case letter. The OP wanted to match the pair. So the[
and]
should not be in your sed pattern. Jul 1, 2020 at 18:54