I am trying to use this command ls -p | egrep "\<[A-Z]+\>" to print all uppercase file and dir names with dir names having / appended. I do not understand why my command works even though I said to grep only file/dir names with all upper case letters. I get the correct output but don't understand how a dir like XXX/ is also listed by grep.

  • By default, grep outputs lines that contain the pattern. To output only whole-line matches, you would need to add -x, or anchor the pattern with ^ and $. Oct 20, 2019 at 0:26
  • In what way does grep "disregard the output"? How does what it produces differ from what you expect it to produce? Oct 20, 2019 at 2:08
  • Why not parse ls?
    – Cyrus
    Oct 20, 2019 at 19:35

3 Answers 3


I presume that you question is "why does DIRNAME/ match the extended regular expression \<[A-Z]+\>, even though it has a character in it that is not an uppercase letter (/)?".

Your regular expression matches anything that contains a word that is all uppercased:

$ printf '%s\n' "this is not matched" "this IS matched" | egrep "\<[A-Z]+\>"
this IS matched

The \< and \> matches a zero-width "word boundary", i.e. the space between a "word character" and a character that is not of that type (or the start/end of the line). A word character is any character in the class [[:alpha:]_] (letters and underscore).

Your expression matches something like DIRNAME/ since DIRNAME matches the expression (there's a word boundary before the D and after the E).

To filter out specific names from a directory listing, don't use grep or other line-based text manipulation tools. Filenames can contain newlines, so line-based tools would have a hard time doing the right thing unless you impose restrictions on filenames.

Instead, to get all uppercase names in a directory in bash:

$ ls -p -d *
DIR/     FILE     TEST123  dir/     file     test123
$ ls -p -d !(*[[:lower:]]*)
DIR/     FILE     TEST123

This requires shopt -s extglob to enable extended globbing patterns. The extended globbing pattern !(*[[:lower:]]*) matches anything that does not contain any lowercase letters.

Note that the pattern does not care about the / that ls -p adds to directory names. This is because the pattern matches filenames, and there is no filename containing the character /. Also, the pattern is expanded before ls is invoked.

If you additionally want to weed out names containing digits, use

$ ls -p -d !(*[[:lower:][:digit:]]*)

(this excludes any name that contains at least one lowercase letter or digit) or,

$ ls -p -d !(*[[:lower:]]*|*[[:digit:]]*)

(this excludes any name that contains at least one lowercase letter, and also excludes any name that contains at least one digit).


The syntax \> means match at end of word. What you want is probably

ls | egrep "^[A-Z.]+$"

where ^ matches at the beginning of the line and and $ matches at the end of the line.

$  ls -p | egrep "[A-Z]+"
$  ls -p ???             
ABC  fif  out

For me it was the word-boundary matching that changed the behaviour.

ls -p | egrep "\<[A-Z]+\>" gives no output in my case.

Compare ls -f |cat and ls -p |cat. When you start tweaking your regex you will find out: one shouldn't bother with these things: too complicated, and in the end it is not advised to parse ls systematically: ls output is only a "report", not a list of files to work with i.e. to do operations on. (Like a screenshot of a "explorer" window in MS Windows, almost.)

$  ls -p |cat 

$  ls -f |cat 

Even the ordering is different; that slash / should not be used to filter directories.

It takes something like find . -type d (lists only directories, in all subdirs also) and then you have a good starting point for any filesystem search.

$  find . -type d 

Your specifation is right between a simple ls (maybe with a grep trick) and some clean solution with find. I don't give one - all depends on your temperament and your future plans regarding listing of "raw" filesystem info.

This illustrates the difference:

 $  find . -name "d1"

$  ls -R |grep d1

With ls-grep you get "additional" lines and characters. In a desperate situation, in the shell interactively: why not? (you know that file "d1" is somewhere)

But anything scripted should rely on find and it's many possibilities. It turns your filesystem into a database: query and report: two steps. ls is only the quick all-in-one tool on the command line.

$ ls -p -d !(*[[:lower:]]*|*[[:digit:]]*)

Is this really serious? What about some strategical advice, instead of this? See comments etc.

O maybe you want to be around for every one liner any user ever could come up with. So baroque. Me I do roots style.

  • what system are you running where egrep "\<[A-Z]+\>" doesn't work? The word-break matches aren't standard, so it's possible they don't work everywhere, but they do seem to be well-supported in Linux (and FreeBSD, if I understand correctly)
    – ilkkachu
    Oct 20, 2019 at 8:36
  • @ilkkachu I have "normal" arch. My /usr/bin/egrep is just exec grep -E ..., and that is GNU grep 3.3. ls -p | cat -A shows all the colors (as line noise) of course. I just copy pasted. This file-and-dir-with-slash thing is a non-problem. dmesg -L=always |less -R or ls -R |less -R I find more interesting. (regex is a "separate" thing, only quite connected to ls -- mantra is dont parse (OK but what else?))
    – user373503
    Oct 20, 2019 at 9:22
  • right, it's the colors that break it for you. Try it with ls --color=auto | grep .... if you have ls aliased to ls --color.
    – ilkkachu
    Oct 20, 2019 at 9:37
  • @ilkkachu Either you are a very clever bot or a not so clever teacher. I am neither bot, nor "not so clever", nor a teacher. Why on earth should I try this? What about my answer? Too long? Not answering the Q? I have to read a link first?
    – user373503
    Oct 20, 2019 at 9:44
  • I didn't say you're a bot, I only suggested that the coloring might be the difference between your case and that of the OP. That is, in case you care and didn't already find out a reason for that. If you don't care, or you already knew it, then nevermind.
    – ilkkachu
    Oct 20, 2019 at 9:54

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.