3

I have file called file.txt. In this file there are words composed of upper and lowercase letters, also there are words consist of upper or lowercase letters and numbers. I would like to filter this file, so the output is free of the words that contain both upper and lower case letters. For example, the input file.txt:

Aaa
aBb
aB
Aa12
12aA
123
123Ab
AAA
aaa

In this file there are words with upper and lowercase letters (e.g. Aaa, aBp), and words contain upper/lower case letters AND digits (e.g. 123Ab). In addition, to words contain only small letters (e.g. aaa), or only capital letters (e.g. AAA). I would like to remove only the words that contain upper AND lowercase letters (e.g. Aaa, aBp), so the output is as follows:

Aa12
12aA
123
123Ab
AAA
aaa

Any ideas?

7
  • 1
    Hi Ahmad, do you mean you would like to remove capital and lowercase letters and keep digits?
    – user88036
    Sep 20, 2018 at 22:25
  • 1
    This reads like homework for school? You should at least have a try, maybe man sed for starters
    – guiverc
    Sep 20, 2018 at 22:31
  • Hi I want to keep the lines with digits , only lower, oly capital and digit with lower or capital both
    – Ahmed
    Sep 20, 2018 at 22:33
  • Hi Goro plz help as my example output above
    – Ahmed
    Sep 20, 2018 at 22:35
  • 1
    Please try harder to explain more clearly what you want.  I don’t understand your problem statement, and I can’t figure it out from the example.   Please do not respond in comments; edit your question to make it clearer and more complete. Sep 20, 2018 at 22:45

4 Answers 4

5
grep -Exv '[A-Za-z]*([A-Z][a-z]|[a-z][A-Z])[A-Za-z]*'

Explanation

  • The idea is to match the opposite of what you want first, i.e. those lines that contain only upper- and lower-case letters. This uses grep -Ex, i.e. grep with extended regex, match the whole line. The -v flag then negates the regex, i.e. return those lines that do not match the following regex.
  • The central part ([A-Z][a-z]|[a-z][A-Z]) matches a single upper-case letter followed by a lower-case letter, or vice versa.
  • The outer part [A-Za-z]*...[A-Za-z]* means that the rest of the line must comprise upper- or lower-case letters only.
5
  • 1
    Thanks @Ahmed. If this answered your question, please click the tick on the left to accept it, and optionally upvote with the up arrow if you like.
    – Sparhawk
    Sep 21, 2018 at 0:07
  • 1
    Based on your interpretation of the question, your answer fails for inputs like DeF that begin and end with uppercase letters, but have lowercase letter(s) in the middle. Sep 21, 2018 at 0:49
  • 1
    @G-Man Oops you are right. Forgot the parens. Thanks, I've fixed it.
    – Sparhawk
    Sep 21, 2018 at 0:52
  • 2
    Ah, so that’s what you meant. I believe that you can leave out the +s and simplify the command to grep -Exv '[A-Za-z]*([A-Z][a-z]|[a-z][A-Z])[A-Za-z]*'.   P.S. The OP doesn’t have the privilege to vote yet. Sep 21, 2018 at 1:52
  • @G-Man Good idea. I basically built the regex from the inside (point 2), before realising I needed point 3 as well. Thanks for the advice (and the privilege info too).
    – Sparhawk
    Sep 21, 2018 at 2:04
1

With grep and assuming one word per line:

grep -E '[[:digit:]]|^([[:lower:]]+|[[:upper:]]+)$'

To report all the matching words in a text, with potentially several words per lines, words delimited by non-word characters:

<text tr -cs '[:alnum:][:digit:]_' '[\n*]' |
  grep -E '[[:digit:]]|^([[:lower:]]+|[[:upper:]]+)$'

Note that you need a POSIX compliant tr implementation, GNU tr won't do. On GNU systems, you can use sed instead:

<text sed -E 's/\W+/\n/g' |
  grep -E '[[:digit:]]|^([[:lower:]]+|[[:upper:]]+)$'
0

To restate your requirements, you want to keep a word if:

  • it contains a digit non-letter, or
  • it is all uppercase letters, or
  • it is all lowercase letters

Then

awk '/[^[:alpha:]]/ || /^[[:upper:]]+$/ || /^[[:lower:]]+$/' file
0

Using sed:

$ sed -E -e '/[0-9]/b' -e '/^[A-Z]+$/b' -e '/^[a-z]+$/b' -e 'd' <file
Aa12
12aA
123
123Ab
AAA
aaa

Annotated sed script:

/[0-9]/b        # Digits are present, branch to end
/^[A-Z]+$/b     # Only uppercase characters present, branch to end
/^[a-z]+$/b     # Only lowercase characters present, branch to end
d               # Delete line, start next cycle
                # (at end, implicit print)

Alternatively,

sed -E -e '/[[:digit:]]/b' -e '/^[[:upper:]]+$/b' -e '/^[[:lower:]]+$/b' -e 'd' <file

There may be a difference between this and the first sed script depending on your locale.

1
  • There's no guarantee that [A-Z] won't match on lower case characters (or even collating elements made of several characters some of which are lower case), For instance, on Solaris 11.4, in the en_US.UTF-8 locale, [A-Z] matches on 759 different uppercase characters, and 846806 lower case ones (well, characters that Solaris considers lowercase, but that also includes bcd...z as they sort between A and Z). Solaris sed doesn't support -E though. Sep 30, 2018 at 12:42

You must log in to answer this question.

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