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?

  • 1
    Hi Ahmad, do you mean you would like to remove capital and lowercase letters and keep digits? – user88036 Sep 20 '18 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 '18 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 '18 at 22:33
  • Hi Goro plz help as my example output above – Ahmed Sep 20 '18 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. – G-Man Sep 20 '18 at 22:45
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.
  • 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 '18 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. – G-Man Sep 21 '18 at 0:49
  • 1
    @G-Man Oops you are right. Forgot the parens. Thanks, I've fixed it. – Sparhawk Sep 21 '18 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. – G-Man Sep 21 '18 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 '18 at 2:04
1

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
1

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.

  • 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. – Stéphane Chazelas Sep 30 '18 at 12:42
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:]]+)$'

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.