I am looking for the correct Perl one-liner to remove all instances of a particular regular expression from a text file.

Namely, I want to remove all instances of [ immediately followed by a capital letter, then followed by any number of characters and spaces until ]. For instance, [CP 5.491] or [MS 283: 56 (variant) in Colapietro, 1989, p. XIV].

I would like to accomplish this in Perl because the simple script I wrote to process my files is otherwise already in that language.


After trying out the two perfectly adequate answers below, I realized that I made one mistake in my original question (sorry!): I would also need to delete instances where the left bracket is followed by cf instead of a capital letter, for instance [cf. CP 2.282]. How would I add that parameter?


To remove:

  • a [ character (\[)
  • immediately followed by a capital letter ([A-Z] Ascii only)
  • or (alternation) by an string cf ( ([A-Z]|cf) ).
  • then followed by any number of characters and spaces ([^]] not a ])
  • until a ] (\]).

You could use (in Perl) either of:

\[([A-Z]|cf)[^]]\]        # Ascii uppercase, avoid `]`
[[]([A-Z]|cf)[^]][]]      # A bit more confusing expression of the same.
[[]([A-Z]|cf).*?[]]       # Use a lazy quantifier (the shorter match).
[[](\p{Lu}|cf).*?[]]      # Unicode property: Letter Uppercase.
\[(\p{Lu}|cf).*?\]        # Probably easier to read.

If you do not use the negated range expression ([^]]) or the lazy match (.*?) the expression would match this whole string:

this part [CP 5.491] or this part [cf 283: 56 in Colapietro, 1989, p. XIV]

Not each part.

perl -pe '$_ =~ s/\[(\p{Lu}|cf).*?\]//g' file
  • Thank you for that exhaustive answer, your solution does exactly what I asked for! However, I realized that I forgot something in my original question, see my latest edit.
    – Mindforms
    Apr 26 '20 at 19:25
  • @Mindforms Answer edited....
    – ImHere
    Apr 26 '20 at 19:34
  • Awesome, thank you again, it works perfectly, and better still, I get it... I had totally forgotten about (...|...) but that edit seems so simple now.
    – Mindforms
    Apr 26 '20 at 19:53

Assuming only the ASCII character set of uppercase letters, this may suffice when you want to remove the matching line in its entirity:

perl -ne 'print unless m{\[([A-Z]|cf).*\]}' file

If you want only to excise the sections like "[CP 5.491], leaving the surrounding text:

perl -pe 's{\[([A-Z]|cf).*?\]}{}g' file
  • Thank you, that second one-liner is indeed exactly what I was looking for! That being said, I would like to understand what is the difference between the solution of @Isaac above (i.e. perl -pe '$_ =~ s/\[[A-Z].*?\]//g' file) and yours. :)
    – Mindforms
    Apr 26 '20 at 19:27
  • $_ =~ s/... is the same as simply s/... in this code. $_ is the default upon which the substitution operates. @Isaac does a nice job of showing you different matching methods. The key here is to use a lazy match .*? as I did, or a negated range expression [^]] as he did. I prefer the lazy match as it looks less "noisy".
    – JRFerguson
    Apr 26 '20 at 19:45
  • I see, thanks! Indeed, the code works the same without $_ =~ . I have a bit of Googling to do to properly understand what that default variable does, but there seems to be no lack of information about it.
    – Mindforms
    Apr 26 '20 at 19:59
  • @Mindforms As for $_ see here.
    – JRFerguson
    Apr 26 '20 at 20:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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